0 介绍
树的公共祖先问题(LCA)是一个经典的问题。所谓公共祖先,它的定义如下。
对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。
–摘自百度百科
如图片所示,0和5的公共祖先是2。
这个问题一般有两种形式,一种是问一颗二叉搜索树某两个节点的公共祖先,一种是问普通的树上两个节点的公共祖先。下面我们来介绍一下解法。
1 二叉搜索树上的LCA
leetcode235题考查了这个问题。
对于二叉搜索树的情况来说,我们要利用好二叉搜索树的性质:根节点左子树上的节点小于根节点的值,根节点右子树上的值均大于根节点。明白这个不难得出方法:
(1)如果两个节点p、q的值均小于root,就从root的左子树继续搜索
(2)如果两个节点p、q的值均大于root,就从root的右子树继续搜索
(3)若不满足(1)、(2),root就是最近公共祖先,在这里root可能是p或q,也可能是其他节点(p、q分散在这个节点两边)。
不难写出程序:
/**
* 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(p->val < root->val && q->val < root->val)
return lowestCommonAncestor(root->left,p,q);
if(p->val > root->val && q->val > root->val)
return lowestCommonAncestor(root->right,p,q);
return root;
}
};
2 普通树上的LCA
leetcode236题讲的是这个问题。普通树上的LCA问题复杂了不少,解决问题的思路是分治与回溯。
整棵树自底向上遍历,对于某一节点,满足以下条件的话,它就是最近公共祖先节点:
(1)p、q分别在它左右两边
(2)这一节点就是p(q),且q( p )在它的子树上。
/**
* 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;
}
bool dfs(TreeNode* root,TreeNode* p,TreeNode* q)
{
if(root == nullptr)
return false;
bool res = false;
if(root->val == p->val || root->val == q->val)//用res标记它是否是p或q
res = true;
bool leftExist = dfs(root->left,p,q);
bool rightExist = dfs(root->right,p,q);//判定子树上是否存在p或q
if((leftExist && rightExist) || (res && leftExist) ||(res && rightExist))
ans = root;
if(leftExist || rightExist)
return true;
return res;
}
};