最近公共祖先
力扣235/LCR 193. 二叉搜索树的最近公共祖先
力扣236/LCR 194. 二叉树的最近公共祖先
核心点在于分清楚可能存在的三种情况:
p,q
位于最近祖先的左右子树p,q
位于最近祖先的左子树p,q
位于最近祖先的右子树
1 二叉搜索树
对于二叉搜索树,我们可以根据其特性以及p,q
的值关系来判断属于以上哪种情况。如:
p,q
都小于root
,则满足情况2p,q
都大于root
,则满足情况3- 其他情况,则
p,q
位于root
的两侧,说明root
位于“分岔路口”,满足情况1
若保证p
小于q
(或p
大于q
),可以简化判断逻辑。即root
小于较小值则搜寻右子树,root
大于较大值则搜寻左子树。
思路:首先使得p
小于q
。然后根据值的大小关系深入迭代左右子树,直到“分岔”,此时root
就是最近公共祖先。
时间复杂度为O(N)
,N为二叉树节点数,最坏情况下,需要遍历所有节点,如链树。
代码如下,其中返回值代表的是最近公共祖先:
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(p->val > q->val)
swap(p, q);
while(root) {
if(q->val < root->val) // 较大值小于root,说明位于左子树
root = root->left;
else if(p->val > root->val) // 较小值大于root,说明位于右子树
root = root->right;
else break;
}
return root;
}
};
递归写法:
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(p->val > q->val)
swap(p, q);
return dfs(root, p, q);
}
TreeNode* dfs(TreeNode* &root, TreeNode* &p, TreeNode* &q) {
if(!root)
return NULL;
if(q->val < root->val)
return dfs(root->left, p, q);
else if(p->val > root->val)
return dfs(root->right, p, q);
return root;
}
};
2 二叉树
对于二叉树,没有二叉搜索树的性质可以使用,也就没法选择一个子树迭代,需要分头行动(递归左右子树)。同时,由于递归是顺序进行的,所以一旦遇到p
或者q
,则他自己就是离自己最近的祖先,就不用继续递归其后面的节点了。
思路:
- 某
root
等于p
或q
,则直接返回该root
,代表已判断存在,不需要再继续找其左右子树了。(判断存不存在就行了,判断某root
是不是最近祖先的核心还是根据最上面的三点) - 否则继续判断某
root
判断其左右子树是否存在p,q
,得到结果left,right - 通过left,right的关系来判断某
root
是不是最近公共祖先- left不存在,right不存在,返回
NULL
(叶子结点)。 - left存在,righ存在,说明某
root
就是最近公共祖先。如当3
为root
时,p,q
分别为5,0
的情况。 - left存在,right不存在,说明
p,q
都在某root
的左子树(右边找完了但没有,左边虽然没找完【因为找到一个就直接返回了】,但是另一个肯定在左边,同理下一条),所以left就是最近公共祖先。如当3
为root
,p,q
分别为5,2
的情况。 - right存在,left不存在,说明
p,q
都在某root
的右子树,所以right就是最近公共祖先。如当5
为root
,p,q
分别为2,4
的情况
- left不存在,right不存在,返回
上述判断left和right的逻辑略微复杂,逻辑简化的时候也需要注意下小细节,见代码。
时间复杂度为O(N)
,N为二叉树节点数,最坏情况下,需要遍历所有节点,如p,q
分别为4,8
或链树。
代码如下:
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
return dfs(root, p, q);
}
TreeNode* dfs(TreeNode* &root, TreeNode* &p, TreeNode* &q) {
if(!root || root == p || root == q)
return root;
TreeNode* left = dfs(root->left, p, q);
TreeNode* right = dfs(root->right, p, q);
if(!left && !right) // 都不存在
return NULL;
if(!left) // left不存在,说明right存在,直接返回right
// 注意不能 if(left) return left,因为若right也存在,则应该返回root。第一条if只能保证后续left和right不同时为空
return right;
if(!right) // right不存在,说明left存在,直接返回left
return left;
return root; // (left && right) 的情况,root为最近公共祖先
}
};