二刷随想录的时候发现,二叉树的公共祖先问题有一个点很巧妙,在这里记录下来,也作为之后复习用的笔记。
记录内容就以随想录的两道题为主。
1、二叉树的最近公共祖先
对应题目236. 二叉树的最近公共祖先 - 力扣(LeetCode)
最近公共祖先的概念不难理解,重点其实就在如何判断一个节点是要找的节点的公共祖先。
如果是,怎么判断是最近的公共祖先。
当使用后序遍历时,如果一个节点的孩子节点同时包含两个目标节点,那么该节点就是目标节点的公共祖先,且一定是最近的公共祖先。
为什么想到用后序遍历,因为中逻辑要处理左右孩子的返回结果,所以一定要使用后序遍历。
那么之后就是处理中逻辑,中逻辑无非就是三种情况:
1、左右孩子返回结果都为空,说明都不包含目标节点,向上返回null。
2、左右孩子返回结果只有一个为空,那么向上返回不为空的结果,因为不为空的结果包含了目标节点。
3、左右节点都不为空,说明该节点的孩子中包含了所有目标节点,该节点就是最近公共祖先,向上返回该节点。
一开始会发现,这三种情况当中,没有包含如果目标节点本身是公共祖先的情况,那么其实在if的终止条件里,已经有包含了这种情况了。
因为if是遍历到节点就直接判断,因此可以认为是自顶向下判断,当遍历到目标节点,并且该节点就是公共祖先时,直接向上返回当前节点,所以结果是完全正确的。
即使还有一个目标节点没有遍历,也无关紧要了,这里是我认为很妙的一个点。
搜索的大致过程如下
代码如下
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
// 遇到pq就返回
// 其实这里就包括了祖先是自己的情况了
// 如果祖先是自己 那么if是自上而下判断 判断到root是p或q 此时p或q就是祖先 会直接返回
// 那么另外一个是否遍历到也无关紧要了
if (root == null || root == p || root == q) return root;
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
// 返回都不为空 则当前节点就是祖先
if (left != null && right != null) return root;
else if (left == null) return right; // 其中一个不为空 返回不为空的那个
else if (right == null) return left;
return null;
}
}
2、二叉搜索树的最近公共祖先
对应题目235. 二叉搜索树的最近公共祖先 - 力扣(LeetCode)
说到搜索二叉树,那么必然就是要提到有序性了。
因为搜索树的有序性,如果某个节点的数值在目标节点的数值范围之内,那么该节点一定是最近公共祖先,可以直接返回。
那么直接处理中逻辑就可以了,不需要处理左右孩子的返回结果,遍历顺序无要求。
代码如下
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
// root == p || root == q条件不能去掉 包含了公共祖先是自己的情况
if (root == null || root == p || root == q) return root;
// root.val在pq的范围内 一定是公共祖先 且是最近的
if (root.val < Math.max(p.val, q.val) && root.val > Math.min(p.val, q.val)) {
return root;
}
else if (root.val < Math.min(p.val, q.val)) {
return lowestCommonAncestor(root.right, p, q);
}
return lowestCommonAncestor(root.left, p, q);
}
}