题目描述
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]
示例:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。
题目链接: https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/
思路1
如果一个节点 node 是节点 p 和节点 q 的公共祖先,则 node 和 p、q 满足下面 3 种情况:
- node->val == p->val,q 在以 node 为根的子树里;
- node->val == q->val,p 在以 node 为根的子树里;
- node->val != p->val && node->val != q->val,则 p 在 node 的左子树里且 q 在 node 的右子树里 或者 p 在 node 的右子树里且 q 在 node 的左子树里。
题目要求找到最低的公共祖先,所以我们要遍历树中所有的节点,判断每个节点是否满足条件。下面的代码使用 bfs 遍历。在遍历过程中,越晚遍历意味着节点越深,所以记录满足条件且最晚遍历到的节点作为答案。代码如下:
/**
* 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* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root==nullptr) return nullptr;
queue<TreeNode*> qq;
qq.push(root);
TreeNode* ans = nullptr;
while(!qq.empty()){
TreeNode* curNode = qq.front(); qq.pop();
if(curNode->val==p->val){
if(search(curNode->left, q) || search(curNode->right, q)) ans = curNode;
}else if(curNode->val==q->val){
if(search(curNode->left, p) || search(curNode->right, p)) ans = curNode;
}else{
if((search(curNode->left, p)&&search(curNode->right, q)) || (search(curNode->left, q)&&search(curNode->right, p))) ans = curNode;
}
if(curNode->left!=nullptr) qq.push(curNode->left);
if(curNode->right!=nullptr) qq.push(curNode->right);
}
return ans;
}
/*在树root中查找是否存在值等于target->val的节点,存在返回true,不存在返回false*/
bool search(TreeNode* root, TreeNode* target){
if(root==nullptr) return false;
if(root->val==target->val) return true;
return search(root->left, target) || search(root->right, target);
}
};
- 时间复杂度:O(n^2)
n 为节点个数,bfs 遍历需要 O(n),bfs 遍历每一个节点还需要 O(n) 的时间来判断,所以为 O(n^2). - 空间复杂度:O(n+h)
n 为节点个数,h 为树高。
思路2
换一种思路递归。在 root 的左右子树中寻找 p 或者 q。在左子树中寻找的结果记为 left,在右子树寻找的结果记为 right。如果 left 为空,说明两个节点都在右子树当中:一个节点是右子树的根节点,另一个节点是右子树的子节点,返回 right。同理,如果 right 为空,则返回 left;如果 right 和 left 都不为空,则返回 root。代码如下:
/**
* 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* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root==nullptr) return nullptr;
if(root==p || root==q) return root;
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
if(left==nullptr) return right;
if(right==nullptr) return left;
if(left!=nullptr && right!=nullptr) return root;
return nullptr;
}
};
- 时间复杂度:O(n)
- 空间复杂度:O(n)