題目:求二叉樹中兩個節點的最近公共祖先節點
一、該二叉樹爲搜索二叉樹
搜索二叉樹的特點:
任意一個節點的左子樹的所有節點值都比該節點的值小,其右子樹的所有節點值都比該節點的值大。
解決該問題方法:
從樹的根節點開始和兩個節點作比較,如果當前節點的值比兩個節點的值都大,則這兩個節點的最近公共祖先節點一定在該節點的左子樹中,則下一步遍歷當前節點的左子樹;
如果當前節點的值比兩個節點的值都小,則這兩個節點的最近公共祖先節點一定在該節點的右子樹中,下一步遍歷當前節點的右子樹;這樣直到找到第一個值是兩個輸入節點之間的值的節點,該節點就是兩個節點的最近公共祖先節點。
如圖:
二、該二叉樹爲一般二叉樹,有二叉樹節點中包含指向父節點的指針
- struct BinaryNode
- {
- BinaryNode* _left;
- BinaryNode* _right;
- BinaryNode* _parent;
- int _value;
- BinaryNode(const int& value)
- :_value(value)
- , _left(NULL)
- , _right(NULL)
- , _parent(NULL)
- {}
- };
方法一:
首先給出node1的父節點node1->_parent,然後將node1的所有父節點依次和node2->parent作比較,如果發現兩個節點相等,則該節點就是最近公共祖先,直接將其返回。如果沒找到相等節點,則將node2的所有父節點依次和node1->_parent->_parent作比較......直到node1->_parent==NULL。
代碼如下:
- BinaryNode * GetLastCommonAncestor(BinaryNode * root, BinaryNode * node1, BinaryNode * node2)
- {
- BinaryNode * temp;
- while (node1 != NULL)
- {
- node1 = node1->_parent;
- temp = node2;
- while (temp != NULL)
- {
- if (node1 == temp->_parent)
- return node1;
- temp = temp->_parent;
- }
- }
- }
該算法時間複雜度爲O(n^2),可用另一種O(n)的算法
方法二:
給定的兩個節點都含有父節點,因此,可將這兩個節點看做是兩個鏈表的頭結點,將求兩個節點的最近公共祖先節點轉化爲求兩鏈表的交點,這兩個鏈表的尾節點都是根節點。
如圖:
若查找節點G, H的最近公共祖先節點可轉化爲如圖所示的兩個鏈表的交點,可知兩節點最近公共祖先節點爲B。
代碼如下:
- int Hight(BinaryNode* root, BinaryNode* node)
- {
- int len = 0;
- for (; node != NULL; node = node->_parent)
- len++;
- return len;
- }
- BinaryNode* GetLastCommonAncestor(BinaryNode* root, BinaryNode* node1, BinaryNode* node2)
- {
- if (root == NULL || node1 == NULL || node2==NULL)
- return NULL;
- int len1 = Hight(root,node1);
- int len2 = Hight(root,node2);
- for (; len1 > len2; len1--)
- node1 = node1->_parent;
- for (; len2 > len1; len2--)
- node2 = node2->_parent;
- while (node1 && node2 && node1 != node2)
- {
- node1 = node1->_parent;
- node2 = node2->_parent;
- }
- if (node1 == node2)
- return node1;
- else
- return NULL;
- }
三、該二叉樹爲一般二叉樹,有二叉樹節點中沒有指向父節點的指針
方法一:
1) 找到從根到node1的路徑,並存儲在一個向量或數組中。
2)找到從根到node2的路徑,並存儲在一個向量或數組中。
3) 遍歷這兩條路徑,直到遇到一個不同的節點,則前面的那個即爲最低公共祖先.
如圖,若查找節點G, H的最近公共祖先節點可轉化爲如圖所示的兩個數組中的最後一個相同的節點,可知兩節點最近公共祖先節點爲B。
方法二:
上面的方法雖然是O(n),但是操作依然繁瑣,並且需要額外的空間來存儲路徑。
從根節點開始遍歷,如果node1和node2中的任一個和root匹配,那麼root就是最低公共祖先。 如果都不匹配,則分別遞歸左、右子樹,如果有一個 節點出現在左子樹,並且另一個節點出現在右子樹,則root就是最低公共祖先. 如果兩個節點都出現在左子樹,則說明最低公共祖先在左子樹中,否則在右子樹。
若求G, H節點的最近公共祖先節點,在B的左子書中找到G, B的右子樹中返回H,得到G,H的最低公共祖先B。
代碼如下:
- BinaryNode* GetLastCommonAncestor(BinaryNode* root, BinaryNode* node1, BinaryNode* node2)
- {
- if (root == NULL || node1 == NULL || node2 == NULL)
- return NULL;
- if (node1 == root || node2 == root)
- return root;
- BinaryNode* cur = NULL;
- BinaryNode* left_lca = GetLastCommonAncestor(root->_left, node1, node2);
- BinaryNode* right_lca = GetLastCommonAncestor(root->_right, node1, node2);
- if (left_lca && right_lca)
- return root;
- if (left_lca == NULL)
- return right_lca;
- else
- return left_lca;
- }
該函數當一個節點是另一個節點的祖先時,返回的是離根節點最近的那個節點,要想返回最近公共祖先節點需進行判斷兩節點是否有祖孫關係,參考代碼如下:
簡要說明:若兩節點爲F,D,則判斷出B的左子樹中的D節點後,繼續判斷判斷節點D的左右子樹中是否含有節點F,若有,則返回B,若沒有則繼續判斷右子樹中的另一個節點。
參考代碼如下:
- BinaryNode* GetLastCommonAncestor(BinaryNode* root, BinaryNode* node1, BinaryNode* node2)
- {
- if (root == NULL || node1 == NULL || node2 == NULL)
- return NULL;
- if (node1 == root || node2 == root)
- return root;
- BinaryNode* cur = NULL;
- BinaryNode* left_lca = GetLastCommonAncestor(root->_left,node1,node2);
- if (NULL != left_lca)
- {
- cur = GetLastCommonAncestor(left_lca->_left, node1, node2);
- if (cur ==NULL)
- cur = GetLastCommonAncestor(left_lca->_right, node1, node2);
- if ((cur == node1) && (left_lca == node2) || (cur == node2) && (left_lca == node1))
- return root;
- }
- BinaryNode* right_lca = GetLastCommonAncestor(root->_right, node1, node2);
- if (NULL != right_lca)
- {
- cur = GetLastCommonAncestor(right_lca->_left, node1, node2);
- if (cur == NULL)
- cur = GetLastCommonAncestor(right_lca->_right, node1, node2);
- if ((cur == node1) && (left_lca == node2) || (cur == node2) && (left_lca == node1))
- return root;
- }
- if (left_lca && right_lca)
- return root;
- if (left_lca == NULL)
- return right_lca;
- else
- return left_lca;
- }
測試用例:
- void Test()
- {
- BinaryNode* root = new BinaryNode(1);
- BinaryNode* cur = root;
- queue<BinaryNode*> q;
- BinaryNode* top = NULL;
- q.push(root);
- for (int i = 2; i <= 7; i++)
- {
- if (!q.empty())
- {
- top = q.front();
- if (cur == top->_left)
- {
- cur = new BinaryNode(i);
- top->_right = cur;
- cur->_parent = top;
- q.pop();
- }
- else
- {
- cur = new BinaryNode(i);
- top->_left = cur;
- cur->_parent = top;
- }
- q.push(cur);
- }
- }
- BinaryNode* node1 = root->_left->_left;
- BinaryNode* node2 = root->_left->_right;
- BinaryNode* ancestor = GetLastCommonAncestor(root, node1, node2);
- if (ancestor)
- cout << ancestor->_value << endl;
- else
- cout << "沒有公共祖先" << endl;
- }
測試用例二叉樹
可分別用包含父節點和不包含父節點的方法進行測試。