百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
示例 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 。因为根据定义最近公共祖先节点可以为节点本身。
来自《程序员代码面试指南》。
主函数是一个递归函数,
如果传入参数root这棵树为空或是找到了p,q,那么停止递归调用,当即返回root;(这里相当于是在根节点这一层搜索结点p,q)
否则,(这里相当于是在根节点的下层搜索结点p,q)
先去左树上找找,记录左树上的搜索结果为left;
再去右树上找找,记录右树上的搜索结果为right;
然后,检查left,right空不空:
如果left,right都不为空,说明 分别在root的左树上、右树上找到了p,q;
如果left,right有一个不空,那么返回这个不空的结果,表示把找到的结果向上传;
如果left,right都空了,说明 在当前的这个子树上没有找到,那就把这一“没找到”的结果向上传。
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 && right!=null){
return root;
}
return left!=null?left:right;
}
来自力扣热心网友,我觉得他的思路很清晰。
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root==null) return null;
// check 1: this root is one of them
if(root.val==p.val||root.val==q.val) return root;
// check2: both are in the left tree
if(find(root.left,p)&&find(root.left,q)){
return lowestCommonAncestor(root.left,p,q);
}
// check3: both are in the right tree
if(find(root.right,p)&&find(root.right,q)){
return lowestCommonAncestor(root.right,p,q);
}
// check4: one is in the left tree, while the other is in the right tree
return root;
}
public boolean find(TreeNode root, TreeNode p){// find p in the tree headed by 'root'
if(root==p) return true;
if(root==null) return false;
return find(root.left,p)||find(root.right,p);
}
来自力扣官方题解。
思路:
对于给定的树,建立一张亲子表parent。
对于待查节点1 p, 建立一个集合visited,收集p的祖先们;
然后,检查 待查节点2 q 的祖先是否存在于visited,由于是从位于底层的q开始向上检查,第一个查出来“存在于visited”中的祖先,就是我们要找的 待查节点1和待查节点2的最低公共祖先祖先。
注:如果题目中要求每次查找速度要快时可以考虑这种做法,因为亲子表parent只需要建立一次即可。
HashMap<Integer,TreeNode> parent=new HashMap<>();
Set<Integer> visited=new HashSet<>();
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
parent.put(root.val,null);
dfs(root);
while(p!=null){
visited.add(p.val);
p=parent.get(p.val);
}
while((q!=null)&&(!visited.contains(q.val)) ){
q=parent.get(q.val);
}
return q;
}
public void dfs(TreeNode root){
if(root.left!=null){
parent.put(root.left.val,root);
dfs(root.left);
}
if(root.right!=null){
parent.put(root.right.val,root);
dfs(root.right);
}
}