LeetCode_72_LowestCommonAncestorOfABinaryTree


1. question: 二叉树的最近公共祖先(中等)

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree

示例 1:

示例1

1
2
3
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点 5 和节点 1 的最近公共祖先是节点 3 。

示例 2:

示例2

1
2
3
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出:5
解释:节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。

示例 3:

1
2
输入:root = [1,2], p = 1, q = 2
输出:1

提示:

1
2
3
4
5
树中节点数目在范围 [2, 105] 内。
-109 <= Node.val <= 109
所有 Node.val 互不相同 。
p != q
p 和 q 均存在于给定的二叉树中。

2. answers

这道题注意不要限制思维,还要注意利用二叉树的递归特性。

两个节点的祖先,换个思路想就是祖先的两个子树包含pq。如果左右子树包含了pq节点,那么就说明当前节点是公共祖先。或者左右子树任意一个包含了pq节点,并且当前节点是pq其中一个,也说明当前节点就是公共祖先。并且,因为是最近公共祖先,所以采取自底向上的方式,递归的方法最直接。另外,因为要判断左右节点,最好是采用后序遍历。并且当前节点也有可能是pq,所以需要注意,当前节点也需要判断。

感觉无论这么样,最终还是回归到二叉树的递归特性:子树仍然是二叉树。

代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/** 找公共祖先,其实就是祖先的左右子树中出现了二者节点,也就是自底向上遍历,即递归。先判断左右子树,再比较当前节点,显然是后序递归。
*
* 当当前节点的左右子树中分别出现了节点,则说明当前节点是其公共祖先。因为递归的缘故,所以,一定是最近的公共祖先。
*
* 此时还需要注意的是,一定是祖先有可能是节点本身。似乎只需要加个判断即可。
*/
public class Solution_0071 {

public boolean tag;

// 因为题目保证了每个节点的值都不同。
public TreeNode postOrder(TreeNode root, TreeNode p, TreeNode q) {

// 递归终止条件
if(root == null) return null;

// 递归左子树
TreeNode left = postOrder(root.left, p, q);

// 递归右子树
TreeNode right = postOrder(root.right, p, q);

// 比较左右子树。注意祖先是二者节点的情况
// 根据tag判断是否找到结果,如果找到,则left、right必有一个是结果,一个是null
if(tag) return left == null ? right : left;


// 未找到结果
// 有三种情况,左子树是否包含两个节点,右子树是否包含两个节点,根节点是否是两个节点
// 因此三者8中组合情况,但是左右子树在一定情况下可以组合判断
// TODO 注意,一定返回的结果要么是null,要么不是null,如果不是null,则一定是qp两个节点、或者祖先节点。
// TODO 祖先节点可根据tag来判断,pq两个节点则无需判断,只需判断是否是null即可。
// 1. 根节点是二者之一,左右子树有一个包含两个节点,那么祖先就是根节点
if((root.val == p.val || root.val == q.val) && (left != null || right != null)) {
tag = true;
return root;
}

// 2. 根节点是二者之一,但是左右子树均不包含两个节点,
// 注意,这个条件虽然只判断了根,但是如果到达此处并进入if,则说明组合1不满足,也就是:根节点是,但是左右子树均不是。
if(root.val == p.val || root.val == q.val) {
return root;
}

// 2. 根节点不是二者之一,左右子树分别是,那么祖先就是根节点
// 注意,程序执行到这里,肯定是根不是,下同
if(left != null && right != null) {
tag = true;
return root;
}

// 3. 根不是,左右子树有一个是,或者都不是
if(left != null) return left;
else return right;

}

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {

return postOrder(root, p, q);

}

public static void main(String[] args) {
System.out.println();

TreeNode tn9 = new TreeNode(4);
TreeNode tn8 = new TreeNode(7);
TreeNode tn7 = new TreeNode(8);
TreeNode tn6 = new TreeNode(0);
TreeNode tn5 = new TreeNode(2, tn8, tn9);
TreeNode tn4 = new TreeNode(6);
TreeNode tn3 = new TreeNode(1, tn6, tn7);
TreeNode tn2 = new TreeNode(5, tn4, tn5);
TreeNode tn1 = new TreeNode(3, tn2, tn3);

Solution_0071 s = new Solution_0071();

TreeNode p = new TreeNode(5);
TreeNode q = new TreeNode(4);
TreeNode res = s.lowestCommonAncestor(tn1, p, q);

System.out.println(res.val);
}
}

参考题解,简化后的代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/** 其实上个方法中的代码可以更加简洁,并且无需设置成员变量
*
* 其实无论根节点是pq,其实和左右子树一样,是可以返回它的。
* 1. 如果是,它有可能是祖先,比如整棵树的根节点,最终返回他是没问题的。它也有可能是左右子树,继续递归就好
* 2. 如果不是,那么判断其左右子树,如果左右子树分别是,那么它就是祖先;如果左右子树不是,则在上一步条件就返回null了。
*
* 总体来说,返回的结果有三种情况:一种是null,一种是pq节点,一种是祖先。
*
* 因为是递归,所以,即使找到祖先之后,也要一步步递归返回。而且祖先也有可能是pq,所以pq和祖先是一种情况。
* 并且因为节点值只能出现一次,所以只要找到祖先,另一个子树一定返回的是null。
*
* 所以即使找到祖先,也无需设置成员变量。直接返回即可。
*/
public class Solution_0071_02 {

public TreeNode postOrder(TreeNode root, TreeNode p, TreeNode q) {

if(root == null || root == p || root == q) return root;

TreeNode left = postOrder(root.left, p, q);
TreeNode right = postOrder(root.right, p, q);

if(left == null && right == null) return null;
else if(left == null && right != null) return right;
else if(left != null && right == null) return left;
else return root;


}
}

3. 备注

参考力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 (leetcode-cn.com)代码随想录 (programmercarl.com)


文章作者: 浮云
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 浮云 !
  目录