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

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值