IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 二叉树的递归套路——最低公共祖先 -> 正文阅读

[数据结构与算法]二叉树的递归套路——最低公共祖先

给定一棵二叉树的头节点head,和另外两个节点a和b。返回a和b的最低公共祖先(a和b有可能不在这棵树上)

最低公共祖先:a和b往上走初次交汇的点

方法一

不用二叉树的递归套路:准备一张HashMap和HashSet,在遍历整棵树的时候,HashMap用来存放每个结点和父结点。a结点一直往父结点方向走,沿途全部加入HashSet,然后b结点往上走的过程,第一个在HashSet里的就是两个结点的最低公共祖先。 这个方法缺点很明显,虽然时间复杂度仍然是O(N),但是不够简洁。不仅跑了个递归,而且还填满了一张表,之后还要从指定结点往上回溯,沿途结点加入HashSet,然后用另外一个结点来回溯比较。太麻烦啦!!!

方法二

二叉树的递归套路:按照惯例,我们来列可能性(假设以X为头节点的整棵树)

1)a和b都不在以X为头结点的整棵树上
2)a和b只有一个在以X为头结点的整棵树上
3)a和b都在以x为头结点的树上(又有四种情况)
1》左树右树各一个
2》左树有两个
3》右树有两个
4》x自己是a或b

整合一下,任何子树需要返回信息就是:

public static class Info{
		// a和b最初交汇点,没有交汇点返回null
		public Node ans;
		// 在子树上是否发现a
		public boolean findA;
		// 在子树上是否发现b
		public boolean findB;
		
		public Info(Node n,boolean fa,boolean fb) {
			ans=n;
			findA=fa;
			findB=fb;
		}
}

完整代码:

package com.harrison.class08;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;

public class Code09_LowestAncestor {
	public static class Node {
		public int value;
		public Node left;
		public Node right;

		public Node(int data) {
			this.value = data;
		}
	}

	public static Node lowestAncestor1(Node head, Node a, Node b) {
		if (head == null) {
			return null;
		}
		HashMap<Node, Node> parentMap = new HashMap<>();
		parentMap.put(head, null);
		fillParentMap(head, parentMap);
		HashSet<Node> aSet = new HashSet<>();
		Node cur = a;
		aSet.add(cur);
		while (parentMap.get(cur) != null) {
			cur = parentMap.get(cur);
			aSet.add(cur);
		}
		cur = b;
		while (!aSet.contains(cur)) {
			cur = parentMap.get(cur);
		}
		return cur;
	}

	public static void fillParentMap(Node head, HashMap<Node, Node> parentMap) {
		if (head.left != null) {
			parentMap.put(head.left, head);
			fillParentMap(head.left, parentMap);
		}
		if (head.right != null) {
			parentMap.put(head.right, head);
			fillParentMap(head.right, parentMap);
		}
	}

	public static class Info {
		// a和b最初交汇点,没有交汇点返回null
		public Node ans;
		// 在子树上是否发现a
		public boolean findA;
		// 在子树上是否发现b
		public boolean findB;

		public Info(Node n, boolean fa, boolean fb) {
			ans = n;
			findA = fa;
			findB = fb;
		}
	}

	public static Info process1(Node head, Node a, Node b) {
		if (head == null) {
			return new Info(null, false, false);
		}
		Info leftInfo = process1(head.left, a, b);
		Info rightInfo = process1(head.right, a, b);
		// 如何算是发现这个结点,要么头节点是,要么左树上有,要么右树上有
		boolean findA = (head == a || leftInfo.findA || rightInfo.findA);
		boolean findB = (head == b || leftInfo.findB || rightInfo.findB);
		// 先假设a,b没有交汇点
		// a和b的交汇点在哪?
		// 左树上提前交会 || 右树上提前交会 || 两边都没有提前交会,那就是头节点处交会
		Node ans = null;
		if (leftInfo.ans != null) {
			ans = leftInfo.ans;
		}
		if (rightInfo.ans != null) {
			ans = rightInfo.ans;
		}
		if (ans == null) {
			if (findA && findB) {
				ans = head;
			}
		}
		return new Info(ans, findA, findB);
	}

	public static Node lowestAncestor2(Node head, Node a, Node b) {
		return process1(head, a, b).ans;
	}

	public static Node generateRandomBST(int maxLevel, int maxValue) {
		return generate(1, maxLevel, maxValue);
	}

	public static Node generate(int level, int maxLevel, int maxValue) {
		if (level > maxLevel || Math.random() < 0.5) {
			return null;
		}
		Node head = new Node((int) (Math.random() * maxValue));
		head.left = generate(level + 1, maxLevel, maxValue);
		head.right = generate(level + 1, maxLevel, maxValue);
		return head;
	}

	public static Node pickRandomOne(Node head) {
		if (head == null) {
			return null;
		}
		ArrayList<Node> arr = new ArrayList<>();
		fillPreList(head, arr);
		int randomIndex = (int) (Math.random() * arr.size());
		return arr.get(randomIndex);
	}

	public static void fillPreList(Node head, ArrayList<Node> arr) {
		if (head == null) {
			return;
		}
		arr.add(head);
		fillPreList(head.left, arr);
		fillPreList(head.right, arr);
	}

	public static void main(String[] args) {
		int maxLevel = 4;
		int maxValue = 100;
		int testTimes = 1000000;
		for (int i = 0; i < testTimes; i++) {
			Node head = generateRandomBST(maxLevel, maxValue);
			Node o1 = pickRandomOne(head);
			Node o2 = pickRandomOne(head);
			if (lowestAncestor1(head, o1, o2) != lowestAncestor2(head, o1, o2)) {
				System.out.println("Oops!");
			}
		}
		System.out.println("finish!");
	}
}

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-12-19 18:27:38  更:2021-12-19 18:27:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 16:43:16-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码