❓剑指 Offer 68 - II. 二叉树的最近公共祖先
难度:简单
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科 中最近公共祖先的定义为:“对于有根树 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
为不同节点且均存在于给定的二叉树中。
注意:本题与 236. 二叉树的最近公共祖先 相同。
💡思路:(递归 —— 通俗易懂)
该树不是二叉搜索树,所以题解与235. 二叉搜索树的最近公共祖先不同:
法一:后序遍历
找最近公共祖先,最先想到的应该是从树的叶子节点 从下往上找:找到第一个子树,既包节点括p
,有包括节点q
的根节点,即为我们要找的最近公共祖先:
节点p
和q
既可以是最近公共祖先,也可以在最近公共祖先的左右子树上,这需要分别判断;
- 我们设一个全局变量
ans
保存我们要找的答案:最近公共祖先; - 如果当前节点是
p
和q
的其中一个,就 +1,然后判断加上该节点的左右子树的返回值,如果等于2,即为答案;如果不等于2,则返回以该节点为根节点的子树中节点等于p
或q
的个数。 - 递归即可得到答案!
法二:先序遍历
该题也可以从上往下找:
- 首先判断根节点是否等于
p
或q
,如果相等则返回该节点; - 如果不等则递归该节点的左右子树:
- 如果左右子树的返回值都不为空,则该节点就是最近公共祖先,返回当前节点;
- 如果左右子树返回返回值中有一个不为空,则返回不为空的返回值;
- 如果都为空,则以该节点为根节点的子树中没有一个节点等于
p
或q
,返回null
。
- 最后的返回值即为 最近公共祖先。
代码:(Java、C++)
法一:后序遍历
Java
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
private TreeNode ans = null;
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
dfs(root, p, q);
return ans;
}
public int dfs(TreeNode root, TreeNode p, TreeNode q){
if(root == null) return 0;
int left = dfs(root.left, p, q);
int right = dfs(root.right, p, q);
if(p == root || q == root){
left++;
}
if(left + right == 2){
ans = root;
return 0;
}
return left + right;
}
}
C++
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* ans = nullptr;
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
dfs(root, p, q);
return ans;
}
int dfs(TreeNode* root, TreeNode* p, TreeNode* q){
if(root == nullptr) return 0;
int left = dfs(root->left, p, q);
int right = dfs(root->right, p, q);
if(p == root || q == root){
left++;
}
if(left + right == 2){
ans = root;
return 0;//置0
}
return left + right;
}
};
法二:先序遍历
Java
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);
return left == null ? right : right == null ? left : root;
}
}
C++
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root == nullptr || root == p || root == q) return root;
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
return left == nullptr ? right : right == nullptr ? left : root;
}
};
法二:先序遍历
Java
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);
return left == null ? right : right == null ? left : root;
}
}
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root == nullptr || root == p || root == q) return root;
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
return left == nullptr ? right : right == nullptr ? left : root;
}
};
🚀 运行结果:
🕔 复杂度分析:
- 时间复杂度:
O
(
n
)
O(n)
O(n) : 其中
n
为二叉树节点数;最差情况下,需要递归遍历树的所有节点。 - 空间复杂度:
O
(
n
)
O(n)
O(n) : 最差情况下,递归深度达到
n
,系统使用 O ( n ) O(n) O(n)大小的额外空间。
题目来源:力扣。
放弃一件事很容易,每天能坚持一件事一定很酷,一起每日一题吧!
关注我LeetCode主页 / CSDN—力扣专栏,每日更新!