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