题目:输入两个树结点,求它们的最低公共祖先
题目一:如果这个树是二叉搜索树,二叉搜索树是排序过的,位于左子树的结点都比父结点小,而位于右子树的结点都比父结点大,我们只需要从树的根结点开始和两个输入的结点比较,如果输入两个结点都比根结点小,那么最低的共同父结点一定在当前结点的左子树中,于是下一步遍历当前结点的这左子结点。如果当前结点的值比两个结点都小,那么最低共同父结点一定在当前结点的右子树中,于是下一步遍历当前结点的右子结点。这样在树中从上到下找到的第一个在两个输入结点的值之间的结点,就是最低的公共祖先。
题目二:如果这个树不是二叉搜索树,不是二叉树,只是普通的数。如果树的结点有指向父结点的指针,那么可以转换为求两个链表的第一个公共交点,就是他们的最低公共祖先。
题目三:普通的树,树中的结点没有指向父结点的指针。
我们首先得到一条从根结点到树中某一结点的路径,这就要求在遍历的时候,有一个辅助内存来保存路径.比如我们用前序遍历的方法来得到从根结点到H 的路径的过程是这样的:( 1 )遍历到A,把A 存放到路径中去,路径中只有一个结点A; ( 2 )遍历到B,把B 存到路径中去,此时路径为A->B; ( 3 )遍历到D,把D 存放到路径中去,此,时路径为A->B->D; ( 4 ) :遍历到F,把F 存放到路径中去,此时路径为A->B->D->F;( 5) F 已经没有子结点了,因此这条路径不可能到这结点H. 把F
从路径中删除,变成A->B->D; ( 6 )遍历G. 和结点F 一样,这条路径也不能到达H. 边历完G 之后,路径仍然是A->B->D; ( 7 )由于D 的所有子结点都遍历过了,不可能到这结点H,因此D 不在从A 到H 的路径中,把D 从路径中删除,变成A->B; ( 8 )遥历E,把E 加入到路径中,此时路径变成A->B->E, ( 9 )遍历H,已经到达目标给点, A->B->E 就是从根结点开始到达H 必须经过的路径。
同样,我们也可以得到从根结点开始到达F 必须经过的路径是A->B功。接着,我们求出这两个路径的最后公共结点,也就是B. B这个结点也是F 和H 的最低公共祖先.
为了得到从根结点开始到输入的两个结点的两条路径,需要追历两次树,每边历一次的时间复杂度是O(n).得到的两条路径的长度在最差情况时是0(时,通常情况丁两条路径的长度是O(logn).
思路三代码如下:
bool GetNodePath(TreeNode* pRoot,TreeNode* pNode,list<TreeNode*>&path)
{
if(pRoot==pNode)
return true;
path.push_back(pRoot);
bool found=false;
vector<TreeNode*>::iterator i=pRoot->m_vChildren.begin();
while(!found && i<pRoot->m_vChildren.end())
{
found=GetNodePath(*i,pNode,path);
++i;
}
if(!found)
path.pop_back();
return found;
}
TreeNode* GetLastCommonNode(const list<TreeNode*> path1,const list<TreeNode*> path2)
{
list<TreeNode*>::const_iterator iterator1=path1.begin();
list<TreeNode*>::const_iterator iterator2=path2.begin();
TreeNode* pLast=NULL;
while(iterator1!=path1.end() && iterator2 !=path2.end())
{
if(*iterator1==*iterator2)
pLast=*iterator1;
iterator1++;
iterator2++;
}
return pLast;
}
TreeNode* GetLastCommonParent(TreeNode* pRoot,TreeNode* pNode1,TreeNode* pNode2)
{
if(pRoot==NULL || pNode1==NULL || pNode2==NULL)
return NULL;
lsit<TreeNode*> path1;
GetNodePath(pRoot,pNode1,path1);
list<TreeNode*> path2;
GetNodePath(pRoot,pNode2,path2);
return GetLastCommonParent(path1,path2);
}