【数据结构系列】——最近公共祖先问题
Git中关于最近公共祖先问题的应用:
Git
的rebase
工作方式告诉我们一个经典算法问题:最近公共祖先(LCA)Lowest Common Ancestor
比如:git pull命令,默认是使用merge方式将远端别人修改拉到本地,如果带上参数git pull -r,就会使用rebase的方式将远端修改拉到本地。
两者直观的区别是:merge方式合并的分支会有很多“分支”、而rebase方法合并的分支就是一条直线
对于多人协作,merge方式并不友好,处在dev分支,使用git rebase master,Git会把dev接到master分支上,即
首先找到这两条分支的最近公共祖先LCA,然后从master节点开始,重演LCA到dev几个commit的修改,最后结果就是把dev的分支完全接到master上面。
二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]
示例 1:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
示例 2:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
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);
if(left != null && right !=null)
return root;
if(left == null && right == null)
return null;
return left == null ? right : left;
}
}
二叉搜索树的最近公共祖先
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
示例 1:
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6
解释: 节点 2 和节点 8 的最近公共祖先是 6。
示例 2:
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
输出: 2
解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root.val < p.val && root.val < q.val)
return lowestCommonAncestor(root.right, p, q);
if(root.val > p.val && root.val > q.val)
return lowestCommonAncestor(root.left, p, q);
return root;
}
}
最深叶节点的最近公共祖先
给你一个有根节点的二叉树,找到它最深的叶节点的最近公共祖先。
回想一下:
- 叶节点 是二叉树中没有子节点的节点
- 树的根节点的 深度 为 0,如果某一节点的深度为 d,那它的子节点的深度就是 d+1
- 如果我们假定 A 是一组节点 S 的 最近公共祖先,S 中的每个节点都在以 A 为根节点的子树中,且 A 的深度达到此条件下可能的最大值。
示例 1:
输入:root = [3,5,1,6,2,0,8,null,null,7,4]
输出:[2,7,4]
解释:
我们返回值为 2 的节点,在图中用黄色标记。
在图中用蓝色标记的是树的最深的节点。
注意,节点 6、0 和 8 也是叶节点,但是它们的深度是 2 ,而节点 7 和 4 的深度是 3 。
示例 2:
输入:root = [1]
输出:[1]
解释:根节点是树中最深的节点,它是它本身的最近公共祖先。
示例 3:
输入:root = [0,1,3,null,2]
输出:[2]
解释:树中最深的叶节点是 2 ,最近公共祖先是它自己。
DFS:
TreeNode res = null;
int pre=0;
public TreeNode lcaDeepestLeaves_1(TreeNode root) {
dfs(root,1);
return res;
}
int dfs(TreeNode node,int depth){
if(node==null) return depth;
int left=dfs(node.left,depth+1);
int right =dfs(node.right,depth+1);
if(left==right&&left>=pre){
res=node;
pre=left;
}
return Math.max(left,right);
}
递归:
public TreeNode lcaDeepestLeaves(TreeNode root) {
if (root == null)
return null;
int left = dfs(root.left);
int right = dfs(root.right);
if (left == right)
return root;
else if (left < right)
return lcaDeepestLeaves(root.right);
return lcaDeepestLeaves(root.left);
}
private int dfs(TreeNode node) {
if (node == null)
return 0;
return 1 + Math.max(dfs(node.right),dfs(node.left));
}