描述:给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
1.对于该题的最近的公共祖先定义:对于有根树T的两个节点p、q,最近公共祖先LCA(T,p,q)表示一个节点x,满足x是p和q的祖先且x的深度尽可能大。在这里,一个节点也可以是它自己的祖先。
2.二叉搜索树是若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值; 若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值。
3.所有节点的值都是唯一的。
4.p、q 为不同节点且均存在于给定的二叉搜索树中。
数据范围:
3<=节点总数<=10000,0<=节点值<=10000。
如果给定以下搜索二叉树: {7,1,12,0,4,11,14,#,#,3,5},如下图:
输入:{7,1,12,0,4,11,14,#,#,3,5},1,12
返回值:7
说明:节点1 和 节点12的最近公共祖先是7
输入:{7,1,12,0,4,11,14,#,#,3,5},12,11
返回值:12
说明:因为一个节点也可以是它自己的祖先.所以输出12
二叉搜索树的最近公共祖先:最直观的想法是,二叉搜索树的特点是左子树数值<中间节点数值<右子树数值,那么我们可以根据这一特性来遍历二叉搜索树,分别得到从根节点到节点p的路径数组pathp以及从根节点到节点q的路径数组pathq,然后遍历两个数组得到最后一个相同的节点值,其即为二叉搜索树的最近公共祖先。遍历路径具体实现方法如下:首先将当前值加入路径数组,然后比较当前值和目标值,如果当前值大于目标值,则在当前节点的左子树寻找目标值,反之如果当前值小于目标值,则在当前节点的右子树寻找目标值,当找到当前目标值即返回,注意,是先加入再返回这样的顺序喔!
vector<int> pathp;
vector<int> pathq;
// 二叉搜索树:左<中<右
void traverse(TreeNode* cur, int target, vector<int>& path)
{
// 加入到数组
path.push_back(cur->val);
// 找到目标值
if(cur->val==target)
return;
// 当前小于目标则在右子树
if(cur->val<target)
traverse(cur->right, target,path);
// 当前大于目标则在左子树
else if(cur->val>target)
traverse(cur->left,target,path);
}
int lowestCommonAncestor(TreeNode* root, int p, int q)
{
// 记录结果
int res;
// 找到从根节点到p的路径
traverse(root, p, pathp);
// 找到从根节点到q的路径
traverse(root, q, pathq);
// 找到最后一个相同的节点值
for(int i=0,j=0;i<pathp.size()&&j<pathq.size();i++,j++)
{
if(pathp[i]==pathq[j])
res=pathp[i];
}
return res;
}
优化:既然我们知道二叉搜索树的特性是左子树数值<中间节点数值<右子树数值,那么两个节点假设p
// 在以root为根的二叉搜索树中遍历p和q的最近公共祖先
int lowestCommonAncestor(TreeNode* root, int p, int q)
{
// 题目中说了树不为空 节点值唯一 故这里可以不处理边界条件
// 记录结果 节点值在0~10000之间
int left=-1,right=-1;
// 当前节点值比p和q均大则遍历当前节点的左子树
if(root->val>p&&root->val>q)
{
left=lowestCommonAncestor(root->left, p, q);
if(left!=-1)
return left;
}
// 当前节点值比p和q均小则遍历当前节点的右子树
if(root->val<p&&root->val<q)
{
right=lowestCommonAncestor(root->right, p, q);
if(right!=-1)
return right;
}
// 否则当前节点值在两者之间返回当前值 其包含p是q祖先或者q是p祖先的情况 就可以减少判断
return root->val;
}