剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
难度简单151
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 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, 因为根据定义最近公共祖先节点可以为节点本身。
说明:
- 所有节点的值都是唯一的。
- p、q 为不同节点且均存在于给定的二叉搜索树中。
在明白原理之后,可以删除代码注释,可读性会更高
方法一:迭代
这道题的主要目的就是希望我们返回一个节点(该节点的值大于
p
p
p或
q
q
q中的一个,并且小于
p
p
p或
q
q
q 中的一个),如此以来做起来就十分简单。只需要在root!=NULL
的情况下迭代,当
p
,
q
p,q
p,q的
v
a
l
val
val都小于
r
o
o
t
−
>
v
a
l
root->val
root−>val时,
r
o
o
t
=
r
o
o
t
−
>
l
e
f
t
root=root->left
root=root−>left.当
p
,
q
p,q
p,q都大于
r
o
o
t
−
>
v
a
l
root->val
root−>val时,
r
o
o
t
=
r
o
o
t
−
>
r
i
g
h
t
root=root->right
root=root−>right,否则,返回
r
o
o
t
root
root。这里包含两种情况:
- 第一: r o o t − > v a l = = p − v a l root->val==p-val root−>val==p−val或者 r o o t − > v a l = = q − > v a l root->val==q->val root−>val==q−>val。
- 第二: r o o t − > v a l root->val root−>val大于其中一个,小于另一个。
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
{
//循环终止条件,这里也可以写为while(true) ,因为在遍历到叶子节点时,一定可以找到合适的出口
while (root != NULL)
{
//p和q的值都大于当前节点的值,证明p和q在当前节点的右侧
if (q->val > root->val && p->val > root->val)
//遍历右子树
root = root->right;
//p和q的值都小于当前节点的值,证明p和q在当前节点的左侧
else if (q->val < root->val && p->val < root->val)
//遍历左子树
root = root->left;
else
//两种递归出口
return root;
}
return NULL;
}
剑指 Offer 68 - II. 二叉树的最近公共祖先
此题目与剑指Offer 68 - I 的区别是二叉树不再是二叉搜索树
方法一:递归/先序遍历二叉树
方法的思想就是:在二叉树的根节点左右两边【如上图在根节点3的左右两边】遍历二叉树,搜索 p − > v a l 或 q − > v a l p->val或q->val p−>val或q−>val 。(我们在遍历的过程中,只要遇到相等的节点,直接返回,不必继续查找另外一个节点)这样就会出现以下三种情况:
- 在左子树(节点3的左边子树)中找到了 p − > v a l 或 者 q − > v a l p->val或者q->val p−>val或者q−>val ,在右子树(节点3的右边子树)中也找到了 p − > v a l 或 者 q − > v a l p->val或者q->val p−>val或者q−>val 的其中一个。那么 p 和 q p和q p和q 的最近公共祖先只能是 r o o t root root。
- 在左子树中找到了 p − > v a l 或 者 q − > v a l p->val或者q->val p−>val或者q−>val ,但在右子树中没有找到,那么就说明两个节点都在左子树中,并且最先找到的节点就是 p 和 q p和q p和q 的最近公共祖先。
- 这一情况同第二种情况,在右子树中找到了找到了 p − > v a l 或 者 q − > v a l p->val或者q->val p−>val或者q−>val ,但是在左子树中没有找到 p 和 q p和q p和q 中的任何一个,显然, p 和 q p和q p和q 同时存在于root的右子树中,那么最先找到的这个节点就是 p 和 q p和q p和q 的最近公共祖先。
问题:为什么没有第四种情况,在左子树和右子树中都没有找到?
因为在题目的说明 处写道:“p、q 为不同节点且均存在于给定的二叉树中” 所以无论如何都不会 出现在在左右子树中都没找到的情况。
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
{
//root==NULL 表明没找到,返回root(也就是NULL)
//root->val==p->val||root->val==q->val ,表明找到了p或q其中的一个节点,返回这个节点
if (root == NULL || root->val == p->val || root->val == q->val)
return root;
// 在左子树中寻找的结果
TreeNode* left = lowestCommonAncestor(root->left, p, q);
//在右子树中寻找的结果
TreeNode* right = lowestCommonAncestor(root->right, p, q);
//对寻找结果分类讨论
if (left == NULL&&right!=NULL) //左子树中找到,右子树中没找到
return right;
if (left != NULL && right == NULL) //左子树中没找到,右子树中找到
return left;
if (left && right) //左右子树中都找到
return root;
}