探析求樹中兩個節點p和q的最低公共祖先

題目


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

分析


描述十分簡潔的題目,其實有多種變體;
我們從4種情況來討論:
  1. 情況1,在二叉搜索樹中找節點p和q的最低公共祖先
  2. 情況2,非二叉搜索樹,只是普通的二叉樹,樹中每個節點包含指向父節點的指針
  3. 情況3,非二叉搜索樹,只是普通的二叉樹,且樹中每個節點不包含指向父節點的指針
  4. 情況4,非二叉樹,只是普通的多叉樹,且樹中每個節點不包含指向父節點的指針

情況1,在二叉搜索樹中找節點p和q的最低公共祖先


該情況是最簡單的形式,根據二叉搜索樹的特性,依次從根節點出發遍歷,查找第一個值在p和q中間的節點即可。
/*(1)情況1,在二叉搜索樹中找節點p和q的最低公共祖先*/
/*二叉搜索樹節點結構*/
typedef struct BSTNode {
	int val;
	BSTNode *left;
	BSTNode *right;

	BSTNode(int val) :val(val), left(NULL), right(NULL) {}
};

BSTNode *getLastCommonParent1(BSTNode *root, BSTNode *p, BSTNode *q)
{
	if (root == NULL)
		return NULL;

	BSTNode *ret = root;

	while (ret != NULL)
	{
		//如果兩個節點p,q的值比根節點ret都大,則最低公共祖先在右子樹
		if (ret->val < p->val && ret->val < q->val)
		{
			ret = ret->right;
		}
		//如果兩個節點p,q的值比根節點ret都小,則最低公共祖先在左子樹
		else if (ret->val > p->val && ret->val > q->val) {
			ret = ret->left;
		}
		//如果ret的值在p和q的中間,則ret即是p和q的最低公共祖先
		else {
			break;
		}
	}//while
	return ret;
}

情況2,非二叉搜索樹,只是普通的二叉樹,樹中每個節點包含指向父節點的指針


此時,所給節點p和q均有一條指向跟節點root的鏈表。該題目即可轉換爲兩個鏈表的第一個公共節點。
/*(2)情況2,非二叉搜索樹,只是普通的二叉樹,樹中每個節點包含指向父節點的指針
此時,所給節點p和q均有一條指向跟節點root的鏈表。該題目即可轉換爲兩個鏈表的第一個公共節點。
*/
/*含有父節點指針的普通二叉樹節點結構*/
typedef struct TPNode {
	int val;
	TPNode *left;
	TPNode *right;
	TPNode *parent;

	TPNode(int val) :val(val), left(NULL), right(NULL), parent(NULL) {}
};

TPNode *getLastCommonParent2(TPNode *root, TPNode *p, TPNode *q)
{
	if (root == NULL)
		return NULL;

	int pLen = 0, qLen = 0;
	TPNode *pTmp = p;
	while (pTmp != NULL)
	{
		++pLen;
		pTmp = pTmp->parent;
	}

	TPNode *qTmp = q;
	while (qTmp != NULL)
	{
		++qLen;
		qTmp = qTmp->parent;
	}

	pTmp = p;
	qTmp = q;
	if (pLen > qLen)
	{
		int diff = pLen - qLen;
		while (diff > 0)
		{
			pTmp = pTmp->parent; --diff;
		}//while
	}
	else {
		int diff = qLen - pLen;
		while (diff > 0)
		{
			qTmp = qTmp->parent; --diff;
		}
	}//else

	while (pTmp != qTmp)
	{
		pTmp = pTmp->parent;
		qTmp = qTmp->parent;
	}

	return pTmp;
}

情況3,非二叉搜索樹,只是普通的二叉樹,且樹中每個節點不包含指向父節點的指針


此時,順着一條p和q都在同一邊的鏈子,也就是說,如果p和q都在某節點的左邊,就到左子樹中去查找公共祖先,如果都在右邊就去右子樹去查找公共祖先。要是p和q不在同一邊,那麼就表示已經找到第一個公共祖先。
/*(3)情況3,非二叉搜索樹,只是普通的二叉樹,且樹中每個節點不包含指向父節點的指針
此時,順着一條p和q都在同一邊的鏈子,也就是說,如果p和q都在某節點的左邊,就到左子樹中去
查找公共祖先,如果都在右邊就去右子樹去查找公共祖先。要是p和q不在同一邊,那麼就表示已經找到第一個公共祖先。
*/
/*不含父節點指針的普通二叉樹節點結構*/
typedef struct BTNode {
	int val;
	BTNode *left;
	BTNode *right;

	BTNode(int val) :val(val), left(NULL), right(NULL) {}
};

bool judge(BTNode *root, BTNode *p)
{
	//判斷二叉樹root中是否含有節點p
	if (root == NULL)
		return false;
	if (root == p)
		return true;

	return judge(root->left, p) || judge(root->right, p);
}
BTNode *getLastCommonParent3(BTNode *root, BTNode *p, BTNode *q)
{
	if (root == NULL || !judge(root,p) || !judge(root,q))
		return NULL;

	if (root == p || root == q)
		return root;

	/*p和q不在root的同一邊,說明root即是它們的最低公共祖先*/
	bool isPOnLeft = judge(root->left, p);
	bool isQOnLeft = judge(root->left, q);

	/*如果p和q都在root的左邊*/
	if (isPOnLeft && isQOnLeft)
	{
		return getLastCommonParent3(root->left, p, q);
	}
	//否則如果p和q都在root的右邊
	else if(!isPOnLeft && !isQOnLeft){
		return getLastCommonParent3(root->right, p, q);
	}
	//否則,說明p和q不在同一邊,此時便找到兩者最低公共祖先
	else {
		return root;
	}
}

情況4,非二叉樹,只是普通的多叉樹,且樹中每個節點不包含指向父節點的指針


此時,先求得從根節點到p和q的路徑,轉換爲求鏈表的最後一個公共子節點。
/*(4)情況4,非二叉樹,只是普通的多叉樹,且樹中每個節點不包含指向父節點的指針
此時,先求得從根節點到p和q的路徑,轉換爲求鏈表的最後一個公共子節點。
*/

/*含有父節點指針的普通二叉樹節點結構*/
typedef struct TreeNode {
	int val;
	TreeNode *left;
	vector<TreeNode *> m_vChildren;

	TreeNode(int val) :val(val), m_vChildren(vector<TreeNode*>()) {}
};

//獲取node節點在樹root中的路徑
bool getNodePath(TreeNode *root, TreeNode *node, vector<TreeNode *> &path)
{
	if (root == node)
		return true;

	path.push_back(root);
	bool found = false;
	vector<TreeNode *>::iterator iter = root->m_vChildren.begin();
	while (!found && iter < root->m_vChildren.end())
	{
		found = getNodePath(*iter, node, path);
		++iter;
	}//while

	if (!found)
		path.pop_back();

	return found;
}

TreeNode *getLastCommonNode(vector<TreeNode *> pV, vector<TreeNode *> qV)
{
	if (pV.empty() && qV.empty())
		return NULL;
	vector<TreeNode *>::iterator pIter = pV.begin(), qIter = qV.begin();

	TreeNode *ret = NULL;
	while (pIter != pV.end() && qIter != qV.end())
	{
		if (*pIter == *qIter)
			ret = *pIter;
		++pIter;
		++qIter;
	}//while
	return ret;

}

TreeNode *getLastCommonParent4(TreeNode *root, TreeNode *p, TreeNode *q)
{
	if (root == NULL)
		return NULL;

	vector<TreeNode *> pV, qV;
	getNodePath(root, p, pV);
	getNodePath(root, q, qV);

	return getLastCommonNode(pV, qV);
}







發佈了360 篇原創文章 · 獲贊 348 · 訪問量 112萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章