二叉树的最近公共祖先
1 题目概述
1.1 题目出处
https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/
1.2 题目描述
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 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。因为根据定义最近公共祖先节点可以为节点本身。
说明:
所有节点的值都是唯一的。
p、q 为不同节点且均存在于给定的二叉树中。
2 DFS
2.1 思路
DFS深度优先方式查找给定的节点p和q,有几种情况:
- 情况1,要找的p或q中的一个为当前root,而另一个在左或右子树中,则当前root就是两个指定节点的最近公共祖先
- 情况2,要找的p或q都不是当前root,且分别存在于不同子树,则当前root就是两个指定节点的最近公共祖先
- 情况3,要找的p或q都不是当前root,且都存在于同一个子树,则当前root不是两个指定节点的最近公共祖先,应该继续往那个子树查找。
- 情况4,要找的p或q都不是当前root,且只有一个存在于子树或两个都不存在于子树,则当前root不是两个指定节点的最近公共祖先,应该继续回溯查找其他节点。
2.2 代码
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
private TreeNode ancestor = null;
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
preorder(root, p, q);
return ancestor;
}
// 前序遍历寻找p或q
// 只要在当前root节点及其左右子树找到p或p就返回true
private boolean preorder(TreeNode root, TreeNode p, TreeNode q){
if(root == null){
return false;
}
boolean rootFind = false;
if(p == root || q == root){
rootFind = true;
}
// 分别在左右子树搜索p和q
boolean leftFind = preorder(root.left, p, q);
boolean rightFind = preorder(root.right, p, q);
// 情况1,要找的p或q中的一个为当前root,而另一个在左或右子树中,则当前root就是两个指定节点的最近公共祖先
// 情况2,要找的p或q都不是当前root,且分别存在于不同子树,则当前root就是两个指定节点的最近公共祖先
if((rootFind && (leftFind || rightFind)) || (leftFind && rightFind)){
ancestor = root;
}
// 情况3,要找的p或q都不是当前root,且分别存在于同一个子树或只有一个存在于子树或两个都不存在于子树,
// 则当前root不是两个指定节点的最近公共祖先
// 只要在root或左右子树找到p或q,就返回true
return rootFind || leftFind || rightFind;
}
}
2.3 时间复杂度
O(N)
2.4 空间复杂度
O(N)
- 递归调用栈深度N(斜二叉树) * O(1)辅助空间
3 DFS-优化
3.1 思路
前面DFS效率不错,但是代码有点多,分支复杂,能不能直接用原方法递归求解呢?
3.2 代码
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null){
return null;
}
// 找到p或q
if(root == p || root == q){
return root;
}
// 在左子树查找p和q
TreeNode left = lowestCommonAncestor(root.left, p, q);
// 左子树没有,则肯定在右子树
if(left == null){
return lowestCommonAncestor(root.right, p, q);
}
// 否则说明左子树至少有p或q中的一个,继续看右子树有没有另一个
TreeNode right = lowestCommonAncestor(root.right, p, q);
// 如果右子树没有则说明两个在left及其子树中,且left节点为最近公共祖先
// 否则当前root就是最近公共祖先
return right == null ? left : root;
}
}
3.3 时间复杂度
O(N)
3.4 空间复杂度
O(N)
- 递归调用栈深度N(斜二叉树) * O(1)辅助空间