二叉樹中兩個節點的最低公共祖先

fucangzxx發表於2016-05-31

1、如果此二叉樹為二叉搜尋樹,即樹中所有結點的左子樹的結點都比父結點小,所有結點的右子樹都比父結點大

  對於此種情況,我們只需從樹的根結點開始和輸入的兩個結點進行比較。如果當前結點的值比兩個結點的值都大,那麼最低的共同父結點一定是在當前結點的左子樹中,於是下一步遍歷當前結點的左子結點;如果當前結點的值比兩個結點的值都小,那麼最低的共同父結點一定是在當前結點的右子樹中,於是下一步遍歷當前結點的右子結點。這樣在樹中從上到下找到第一個在輸入結點的值之間的結點,就是最低的公共祖先。

struct BSNode
{
	int _data;
	BSNode* _left;
	BSNode* _right;
	BSNode(int data = 0) :_data(data), _left(NULL), _right(NULL)
	{}
};
BSNode* GetLastCommonParent_1(BSNode* pRoot, BSNode* pNode1, BSNode* pNode2)
{
	if (pRoot == NULL || pNode1 == NULL || pNode2 == NULL)
	{
		return NULL;
	}
	if (pNode1 == pNode2)
	{
		return pNode1;
	}
	BSNode* cur = pRoot;
	while (true)
	{
		if ((cur->_data >= pNode1->_data&&cur->_data<=pNode2->_data) ||
		(cur->_data>=pNode2->_data&&cur->_data <= pNode1->_data))
			return cur;
		else if (cur->_data>pNode1->_data)
		{
			cur = cur->_left;
		}
		else
		{
			cur = cur->_right;
		}
	}
	return NULL;
}

2、如果此樹是含有父結點的二叉樹

對於此種情況,可以轉化為求兩個連結串列的第一個公共結點。假設樹結點中指向父結點的指標是_parent,那麼從樹每一結點開始都有一個由_parent串起來的連結串列,這些連結串列的尾指標都是根結點。輸入兩個結點,那麼這兩個結點位於兩個連結串列上,它們的最低公共祖先剛好就是這兩個連結串列的第一個公共結點。

struct BinTreePar
{
	int _data;
	BinTreePar* _left;
	BinTreePar* _right;
	BinTreePar* _parent;
	BinTreePar(int data = 0) :_data(data), _left(NULL), _right(NULL), _parent(NULL)
	{}
};
//求到根結點的路徑長度
int BinLen(BinTreePar* pRoot, BinTreePar* pNode)
{
	int count = 0;
	BinTreePar* cur = pNode;
	while (cur != pRoot)
	{
		++count;
		cur = cur->_parent;
	}
	return count;
}
BinTreePar* GetLastCommonParent_2(BinTreePar* pRoot, BinTreePar* pNode1, BinTreePar* pNode2)
{
	if (pRoot == NULL || pNode1 == NULL || pNode2 == NULL)
	{
		return NULL;
	}
	if (pNode1 == pNode2)
	{
		return pNode1;
	}
	//類似相交單連結串列找相遇點
	BinTreePar* curA = pNode1;
	BinTreePar* curB = pNode2;

	//計算到根節點的距離
	int lenA = BinLen(pRoot, pNode1);
	int lenB = BinLen(pRoot, pNode2);
	int subLen = lenA - lenB;
	if (subLen > 0)
	{
		while (subLen--)
		{
			curA = curA->_parent;
		}
	}
	else if (subLen < 0)
	{
		while (subLen++)
		{
			curB = curB->_parent;
		}
	}
	while (curA != curB)
	{
		curA = curA->_parent;
		curB = curB->_parent;
	}
	return curA;
}
3、此樹是普通的二叉樹
      對於此種情況,我們需找到從根節點到輸入的兩個的路徑,然後有輔助的記憶體來儲存路徑,然後再從這兩條路徑中找出最後一個相同的結點。

#include <list>
struct BinTree
{
	int _data;
	BinTree* _left;
	BinTree* _right;
	BinTree(int data = 0) :_data(data), _left(NULL), _right(NULL)
	{}
};
//尋找路徑
bool FindRoot(BinTree* pRoot, BinTree* pNode, list<BinTree*>& listRoot)
{
	if (pRoot == NULL || pNode == NULL)
	{
		return true;
	}
	listRoot.push_back(pRoot);
	if (pRoot == pNode)
	{
		return true;
	}
	if (pRoot->_left)
	{
		bool ret = FindRoot(pRoot->_left, pNode, listRoot);
		if (ret)
		{
			return true;
		}
	}
	if (pRoot->_right)
	{
		bool ret = FindRoot(pRoot->_right, pNode, listRoot);
		if (ret)
		{
			return true;
		}
	}
	listRoot.pop_back();
}
BinTree* GetLastCommonParent_3(BinTree* pRoot, BinTree* pNode1, BinTree* pNode2)
{
	//找到由根節點到pNode1、pNode2的路徑,再找最後一個相同的結點,用list儲存路徑
	if (pRoot == NULL || pNode1 == NULL || pNode2 == NULL)
	{
		return NULL;
	}
	if (pNode1 == pNode2)
	{
		return pNode1;
	}
	list<BinTree*> listRoot1;
	list<BinTree*> listRoot2;
	FindRoot(pRoot, pNode1, listRoot1);
	FindRoot(pRoot, pNode2, listRoot2);
	list<BinTree*>::iterator iteA = listRoot1.begin();
	list<BinTree*>::iterator iteB = listRoot2.begin();
	BinTree* ret = listRoot1.front();
	list<BinTree*>::iterator end = listRoot1.end();
	
	while (iteA != listRoot1.end() && iteB != listRoot2.end() && (*iteA)->_data == (*iteB)->_data)
	{
		ret = *iteA;
		++iteA;
		++iteB;
	}
	return ret;
}




相關文章