描述:给定一棵二叉树(保证非空)以及这棵树上的两个节点对应的val值 o1 和 o2,请找到 o1 和 o2 的最近公共祖先节点。
数据范围:树上节点数满足 , 节点值val满足区间 [0,n)。
要求:时间复杂度 O(n)。
注:本题保证二叉树中每个节点的val值均不相同。
如当输入{3,5,1,6,2,0,8,#,#,7,4},5,1时,二叉树{3,5,1,6,2,0,8,#,#,7,4}如下图所示:
所以节点值为5和节点值为1的节点的最近公共祖先节点的节点值为3,所以对应的输出为3。
节点本身可以视为自己的祖先。
输入:{3,5,1,6,2,0,8,#,#,7,4},5,1
返回值:3
输入:{3,5,1,6,2,0,8,#,#,7,4},2,7
返回值:2
在二叉树中找到两个节点的最近公共祖先:最直观的想法是,数组存储法。首先遍历二叉树,分别找到从根节点到o1与从根节点到o2的路径数组,然后比较两个数组找到最后一个相同的元素即为o1和o2的最近公共祖先。在遍历二叉树的过程中,由于要找的是从根节点到某一节点的路径,故使用前序遍历。其与二叉搜索树的区别是,二叉搜索树的前序遍历的左和右部分都是满足条件再进入循环,而该普通二叉树中没有,故左边返回后可能还会进入右边,这时我们就需要加一个标记是否找到的全局变量flag,在递归终止条件中需要加上当找到即flag为true时则返回,在左右递归遍历结束后还需要判断flag是否为true,如果是则表明找到返回即可,否则表示没找到则要将当前元素弹出数组。
vector path1;
vector path2;
bool flag=false; // 标记是否找到
// 求解路径数组
void dfs(TreeNode* root,int o,vector& path)
{
if(flag||!root) return; // 当为空或者已经找到则返回空
path.push_back(root->val);
if(root->val==o)
{
flag=true; // 标记找到 否则后续会继续遍历
return;
}
dfs(root->left,o,path);
dfs(root->right,o,path);
// 找到
if(flag)
return;
// 没找到弹出元素
path.pop_back();
}
int lowestCommonAncestor(TreeNode* root, int o1, int o2)
{
dfs(root,o1,path1);
flag=false; // 重置为空
dfs(root,o2,path2);
int res;
// 记录最后一个相同的
for(int i=0,j=0;i
idea:上述方法是先找到路径,再比较路径,其实可以边遍历边比较。如果节点为空则返回-1,如果当前节点是两者其中之一,则返回当前节点值,然后再依次遍历左子树和右子树,当左子树返回-1则表明在左子树没找到故返回右子树,反之当右子树返回-1则表明在右子树没找到故返回左子树,反之两个都找到了则表明最近公共祖先即为当前节点。
int lowestCommonAncestor(TreeNode* root, int o1, int o2)
{
// 该子树没找到返回-1
if(root==NULL)
return -1;
// 该节点是其中某一个节点
if(root->val==o1||root->val==o2)
return root->val;
// 左子树寻找祖先
int left=lowestCommonAncestor(root->left, o1, o2);
// 右子树寻找祖先
int right=lowestCommonAncestor(root->right, o1, o2);
// 左子树没找到则在右子树上
if(left==-1)
return right;
// 右子树没找到则在左子树上
if(right==-1)
return left;
// 否则是当前节点
return root->val;
}