面试算法(五十)树中两个结点的最低公共祖先

来源:互联网 发布:mac上的好游戏 编辑:程序博客网 时间:2024/06/05 19:10

1、题目:输入两个树结点,求他们的最低公共祖先。

解法:

本题看似是一个题目,其实是一组题目。这就要求我们应该主动地与面试官交流,以获得准确的题目信息。

1)比如,这树是不是二叉树?如果是二叉树,并且是二叉搜索树,是可以找到公共结点的。

假设是二叉搜索树,因为它是排序过的,位于左子树的结点都比父结点小,而位于右子树的结点都比父结点大,我们只需要从树的根结点开始和两个输入的结点进行比较。

如果当前结点的值比两个结点的值都大,那么最低的共同父结点一定是在当前结点的左子树中,于是下一步遍历当前结点的左子结点。如果......都小,那么....右子树中,于是....右子结点。

这样在树中从上到下找到的第一个在两个输入结点的值之间的结点,就是最低的公共祖先。


2)如果这棵树只是普通的树怎么办?那么树的结点中有没有指向父结点的指针?

如果树中的每个结点(除根结点外)都有一个指向父结点的指针,这个问题可以转换为求两个链表的第一个公共结点。

假设树结点中指向父结点的指针是pParent,那么从树的每一个叶结点开始都有一个由指针pParent串起来的链表,这些链表的尾指针都是树的根结点。输入两个结点,那么这两个结点位于两个链表上,他们的最低公共祖先刚好是这两个链表的第一个公共结点。

比如输入的两个结点分别为F和H,那么F在链表F->D->B->A上,而H在链表H->E->B->A上,这两个链表的第一个交点B刚好也是他们的最低公共祖先。


3)假设这颗树是普通的树,并且树中的结点没有指向父结点的指针。

所谓两个结点的公共祖先,指的是这两个结点都出现在某个结点的子树中。我们可以从根结点开始遍历一棵树,每遍历到一个结点时,判断两个输入结点是不是在它的子树中。如果在子树中,则分别遍历它的所有子结点,并判断两个输入结点是不是在他们的子树中。

这样从上到下一直找到的第一个结点,他自己的子树中同时包含两个输入的结点而它的子结点却没有,那么该结点就是最低的公共祖先。

这里可以用辅助内存,用两个链表分别保存从根结点到输入的两个结点的路径,然后把问题转换成两个链表的最后公共结点。

bool GetNodePath(TreeNode* pRoot, TreeNode* pNode, list<TreeNode*>& path){if(pNode == pRoot)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;list<TreeNode*> path1;GetNodePath(pRoot, pNode1, pNode1);list<TreeNode*> path2;GetNodePath(pRoot, pNode2, pNode2);return GetLastCommonNode(path1, path2);}



0 0
原创粉丝点击