面试题50:树中两个节点的最低公共祖先

当每个节点都有指向父节点的指针时,可转换成两个链表的公共祖先。

当该树是一个二叉搜索树时,需要从树的根节点开始于两个输入的节点比较。如果当前节点的值都比两个节点的值大,那么最低的公共祖先一定在该节点的左子树中,如果都小则在右子树中。利用递归直到找到当前结点的值位于两个节点的中间那么这个便是最低的公共祖先;


假设既没有父节点,也不是二叉搜索树,则用下边思路:

假设输入节点是F和H,判断A的子树中是否同时包含F和H,得到的结果是true。接着我们在先后判断A的两个子节点B和C的子树是不是同时包含F和H,结果是B的结果是true而C的结果是false。接下来我们在判断B的两个子结点D和E,发现这两个结点得到的结果都是false。于是B是最后一个公共祖先。

该思路的缺点:会对同一个节点遍历多次,改进的方法如下:

利用两个链表(记录路径)辅助内存实现:(前序遍历)(找到结点H)

1)遍历A,A放入路径

2)遍历B,B放入路径,此时路径中A->B

3)遍历到D,A->B->D

4)遍历到F,A->B->D->F

5)F中没有节点,所以这条路径不可能到达H,把F删除,变成A->B->D

6)遍历G,和结点F一样,遍历完G后,仍是A->B->D

7)删除D,变成A->B

8)遍历E,A->B->E

9)遍历H,已达到目标结点,A->B->E就是从根节点到H必须经过的路。

10)同样可求得从根节点到F的必经之路A->B->D。接着求这两个路径的最后公共结点,也就是B

分析:找到两个路径需要遍历两次,每次遍历的时间复杂度为O(n),两条路径的长度最差是O(n),通常情况下长度是O(logn)。

#include<iostream>
#include<list>
#include<vector>
using namespace std;
//建立普通树的数据类
class TreeNode
{
public:
	char data;
	vector<TreeNode*> childern;
public:
	TreeNode(char);   //构造树的节点
	void PlusChild(TreeNode *); //向当前树增加叶子节点
	int GetData();    //取得当前树的数据
	void Enum();      //遍历当前树
};
TreeNode::TreeNode(char _data)
{
	this->data = _data;
}
int TreeNode::GetData()
{
	return this->data;
}
void TreeNode::Enum()
{
	unsigned int size = this->childern.size();
	printf("%c ", this->data);
	for (unsigned int i = 0; i < size; i++)
		this->childern[i]->Enum();
}
void TreeNode::PlusChild(TreeNode * child)
{
	this->childern.push_back(child);
}
//得到pRoot到节点pNode的路径,保存在path中
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->childern.begin();
	while (!found&&i != pRoot->childern.end())
	{
		found = GetNodePath(*i, pNode, path);
		++i;
	}
	if (!found)
		path.pop_back();
	return found;
}
//获得path1和path2最后一个公共点
TreeNode* GetLastCommonNode(const list<TreeNode*>& path1, const list<TreeNode*>& path2)
{
	list<TreeNode*>::const_iterator iter1 = path1.begin();
	list<TreeNode*>::const_iterator iter2 = path2.begin();
	TreeNode* pLast = NULL;
	while (iter1 != path1.end() && iter2 != path2.end())
	{
		if (*iter1 == *iter2)
			pLast = *iter1;
		iter1++;
		iter2++;
	}
	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, path1);

	list<TreeNode*> path2;
	GetNodePath(pRoot, pNode2, path2);

	return GetLastCommonNode(path1, path2);
}
//测试用例
int main()
{
	TreeNode* pRoot0 = new TreeNode('A');
	TreeNode* pRoot1 = new TreeNode('B');
	TreeNode* pRoot2 = new TreeNode('C');
	TreeNode* pRoot3 = new TreeNode('D');
	TreeNode* pRoot4 = new TreeNode('E');
	TreeNode* pRoot5 = new TreeNode('F');
	TreeNode* pRoot6 = new TreeNode('G');
	TreeNode* pRoot7 = new TreeNode('H');
	TreeNode* pRoot8 = new TreeNode('I');
	TreeNode* pRoot9 = new TreeNode('J');

	pRoot0->PlusChild(pRoot1);
	pRoot0->PlusChild(pRoot2);

	pRoot1->PlusChild(pRoot3);
	pRoot1->PlusChild(pRoot4);

	pRoot3->PlusChild(pRoot5);
	pRoot3->PlusChild(pRoot6);

	pRoot4->PlusChild(pRoot7);
	pRoot4->PlusChild(pRoot8);
	pRoot4->PlusChild(pRoot9);

	pRoot0->Enum();
	TreeNode* result = NULL;

	result = GetLastCommonParent(pRoot0, pRoot5, pRoot6);
	cout << "公共结点的数据是:"<<result->data << endl;
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值