小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
如上图,5 和 1 的最近公共祖先是 3,6 和 4 的最近公公祖先是 5。
解题思路:
以下解题思路以上图中的二叉树为例。首先,跟节点一定是任何两个元素的公共祖先,但不一定是最近公共祖先,或者可以说是「最远公共祖先」。此时,有一下三种情况:
- 两个节点分别在跟节点的左右子树,那么跟节点就是最近公共祖先。
- 两个节点都在跟节点的左子树,那么,跟节点的左子节点也是两个节点的公共祖先(不一定是最近公共祖先)
- 两个节点都在跟节点的右子树,那么,跟节点的右子节点也是两个节点的公共祖先(不一定是最近公共祖先)
因此,我们可以这样找他们的最近公共祖先(两个节点分别是 p 和 q):
- 如果 p 和 q 任何一个节点和 root 是同一个节点,那么最近公共祖先就是 root
- 分别在左子树和又子树找两个节点
- 如果他们都在左子树,那么在左子树寻找他们的最近公共祖先
- 如果他们都在右子树,则在右子树寻找他们的最近公共祖先
- 如果都不在,那么他们的最近公共祖先就是 root
因为从左右子树寻找他们的最近公共祖先和最初的问题相同,则可以通过递归调用。
最终代码:
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null ||root.val == p.val || root.val == q.val) {
return root;
}
if (nodeInTree(root.left, p) && nodeInTree(root.left, q)) {
return lowestCommonAncestor(root.left, p, q);
}
if (nodeInTree(root.right, p) && nodeInTree(root.right, q)) {
return lowestCommonAncestor(root.right, p, q);
}
return root;
}
private boolean nodeInTree(TreeNode root, TreeNode n) {
if (root == null) {
return false;
}
if (root.val == n.val) {
return true;
}
return nodeInTree(root.left, n) || nodeInTree(root.right, n);
}
}
以上用到了两个递归方法,其实可以并做一个。
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode 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) return right;
if (right == null) return left;
return root;
}
}
这个写法可以理解为,从两个节点向上找他们的父节点,直到找到第一个共同的父节点即时我们要找的结果。