标签:code || 怎么 相交 有一个 调用 子节点 pre 函数返回
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
遇到任何递归型的问题,无非就是灵魂三问:
1、这个函数是干嘛的?
情况 1,如果
p
和q
都在以root
为根的树中,函数返回的即使p
和q
的最近公共祖先节点。情况 2,那如果
p
和q
都不在以root
为根的树中怎么办呢?函数理所当然地返回null
呗。情况 3,那如果
p
和q
只有一个存在于root
为根的树中呢?函数就会返回那个节点。
2、这个函数参数中的变量是什么的是什么?
函数参数中的变量是
root
,因为根据框架,lowestCommonAncestor(root)
会递归调用root.left
和root.right
;至于p
和q
,我们要求它俩的公共祖先,它俩肯定不会变化的。把「以
root
为根」转移成「以root
的子节点为根」,不断缩小问题规模
3、得到函数的递归结果,你应该干什么?
先想 base case,如果
root
为空,肯定得返回null
。如果root
本身就是p
或者q
,比如说root
就是p
节点吧,如果q
存在于以root
为根的树中,显然root
就是最近公共祖先;即使q
不存在于以root
为根的树中,按照情况 3 的定义,也应该返回root
节点。
以上两种情况的 base case 就可以把框架代码填充一点了:
TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
// 两种情况的 base case
if (root == null) return null;
if (root == p || root == q) return root;
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
}
用递归调用的结果left
和right
来搞点事情。根据刚才第一个问题中对函数的定义,我们继续分情况讨论:
情况 1,如果
p
和q
都在以root
为根的树中,那么left
和right
一定分别是p
和q
(从 base case 看出来的)。情况 2,如果
p
和q
都不在以root
为根的树中,直接返回null
。情况 3,如果
p
和q
只有一个存在于root
为根的树中,函数返回该节点。
left
和right
非空,分别是p
和q
,可以说明root
是它们的公共祖先,但能确定root
就是「最近」公共祖先吗?
这就是一个巧妙的地方了,因为这里是二叉树的后序遍历啊!前序遍历可以理解为是从上往下,而后序遍历是从下往上,就好比从
p
和q
出发往上走,第一次相交的节点就是这个root
,所以这个root
当然是最近公共祖先了
public class LowestCommonAncestor {
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
// base case
if(root == null) {
return null;
}
// 如果遍历到的根节点为p或者q,说明在父节点的子树下找到了该节点,所以返回该父节点
if(p==root||q==root) {
return root;
}
// 后序遍历,递归调用,left是在父节点的左子树中找到的p节点或者q节点或者空节点,right同理
TreeNode left = lowestCommonAncestor(root.left,p,q);
TreeNode right = lowestCommonAncestor(root.right,p,q);
// 如果左子树和右子树都找到了p,q节点,说明父节点就是最近公共祖先,因为是后序遍历,所以能确保该父节点就是最近的祖先
if(left!=null&&right!=null) {
return root;
}
// 如果左子树和右子树都没有找到p,q节点,说明p,q节点和该父节点没关系,返回null,表示该父节点的子树下不包含p,q节点
if(left==null&&right==null) {
return null;
}
// 在上边条件都不满足的情况下,说明在该父节点下只找到了p或者q其中一个节点,返回这个找到的节点即可,
// 如果是最外层的递归调用可能是因为另一个节点在这个节点的子树下,已经遍历找到了这个节点,就直接返回了没有再去找另一个节点,或者是两个节点都在左子树的某一个父节点下,所以右子树没有找到节点,这里当然返回这两个节点所在树的子调用里的父节点也就是现在返回的left了,右子树同理
return left!=null?left:right;
}
}
标签:code || 怎么 相交 有一个 调用 子节点 pre 函数返回
原文地址:https://www.cnblogs.com/RealGang/p/14891288.html