二叉樹兩個節點的公共節點
轉自:http://blog.csdn.net/hhygcy/article/details/4660362
很流行的一個問題,常見於各種面試中,http://fayaa.com/tiku/view/16/ 這裡有一個很好的彙總.
找尋二叉樹中兩個節點的公共父節點中最近的那個節點
|
情況1. 節點只有left/right,沒有parent指標,root已知 情況2. root未知,但是每個節點都有parent指標 情況3. 二叉樹是個二叉查詢樹,且root和兩個節點的值(a, b)已知 |
雖然情況一是第一個情況,但是看上去比較複雜,我們放到最後來說,先從第二個情況開始說。
10
/ /
6 14
/ / / /
4 8 12 16
/ /
3 5
畫一個二叉樹來做例子。如果我們要找3和8這兩個節點的公共父親節點,我們的做法是首先找到3到根節點的路勁,然後找到8到根節點的路徑。
10
// /
6 14
/ / / /
4 8 12 16
/ /
3 5
3的路徑用紅色表示,8的用綠色表示,可以看到, 這裡的問題實際上是另一個我們熟知的問題,有2個相交的單連結串列,找出它們的相交點!
只要把這個二叉樹的圖片倒過來看,或者把脖子倒過來看就知道了:)那個方法也是傳統的求出linkedList A的長度lengthA, linkedList B的長度LengthB。然後讓長的那個連結串列走過abs(lengthA-lengthB)步之後,齊頭並進,就能解決了。
- int getLength (bstNode* pNode)
- {
- int length = 0;
- bstNode* pTemp = pNode;
- while (pTemp)
- {
- length ++ ;
- pTemp = pTemp->pParent;
- }
- return length;
- }
- bstNode* findLCACase2(bstNode* pNode1, bstNode* pNode2)
- {
- int length1 = getLength(pNode1);
- int length2 = getLength(pNode2);
- // skip the abs(length1-length2)
- bstNode* pIter1 = NULL;
- bstNode* pIter2 = NULL;
- int k=0;
- if (length1>=length2)
- {
- bstNode* pTemp = pNode1;
- while (k++<length1-length2)
- {
- pTemp = pTemp->pParent;
- }
- pIter1 = pTemp;
- pIter2 = pNode2;
- }
- else
- {
- bstNode* pTemp = pNode1;
- while (k++<length2-length1)
- {
- pTemp = pTemp->pParent;
- }
- pIter1 = pNode1;
- pIter2 = pTemp;
- }
- while (pIter1&&pIter2&&pIter1!= pIter2)
- {
- pIter1 = pIter1->pParent;
- pIter2 = pIter2->pParent;
- }
- return pIter1;
- }
自己寫了個程式碼,總覺得有些拖沓冗餘,希望有緣人看到文章之後能幫我改寫的更和諧一些。
還是原來這個圖,情況三,如果是個二叉搜尋樹,而且root和a, b已知,我們這個case假設a,b=3,8。從知道根這個條件我們很自然聯想到遞迴(當然不遞迴也可以)地往下找。關鍵是收斂條件,什麼情況下可以判斷出當然檢查的這個節點是最近父親節點呢?其實從這個例子已經可以看出一些端倪了,如果當前訪問的節點比a,b來的都小,肯定不行。如果比a,b都大,也不行。那也就是說,這個節點只有在a<=node<=b的區間內才成立(我們假定a<b這裡)。這樣的問題,網上廣為流傳著類似的程式碼:
- bstNode* findLCACase3(bstNode* pNode, int value1, int value2)
- {
- bstNode* pTemp = pNode;
- while (pTemp)
- {
- if (pTemp->data>value1 && pTemp->data>value2)
- pTemp = pTemp->pLeft;
- else if(pTemp->data<value1 && pTemp->data<value2)
- pTemp = pTemp->pRight;
- else
- return pTemp;
- }
- return NULL;
- }
好,前面的問題都解決了,我們再回過頭來看第一個情況,只有ROOT和left, right節點,沒有parent也不是排序樹,怎麼辦?網路上也流傳著很多所謂的LCA,RMQ演算法,我們不暇找個最合適的,尤其是在面試的時候,特定時間空間下你很難寫出一個邏輯非常複雜的東西(比如你會在面試的時候去實現一個Suffix Tree還是用動態規劃來求最長公共子串,哪怕效率不同,我也選擇動態規劃:))。所以這裡,碰到類似的問題的時候,我選擇簡單的記錄找到node1和node2的路徑,然後再把它們的路徑用類似的情況二來做分析,比如還是node1=3,node2=8這個case.我們肯定可以從根節點開始找到3這個節點,同時記錄下路徑3,4,6,10,類似的我們也可以找到8,6,10。我們把這樣的資訊儲存到兩個vector裡面,把長的vector開始的多餘節點3扔掉,從相同剩餘長度開始比較,4!=8, 6==6, coooool,我們找到了我們的答案。下面的程式碼完全按照這個思路寫成
- #include <vector>
- bool nodePath (bstNode* pRoot, int value, std::vector<bstNode*>& path)
- {
- if (pRoot==NULL) return false;
- if (pRoot->data!=value)
- {
- if (nodePath(pRoot->pLeft,value,path))
- {
- path.push_back(pRoot);
- return true;
- }
- else
- {
- if (nodePath(pRoot->pRight,value,path))
- {
- path.push_back(pRoot);
- return true;
- }
- else
- return false;
- }
- }
- else
- {
- path.push_back(pRoot);
- return true;
- }
- }
- bstNode* findLCACase1(bstNode* pNode, int value1, int value2)
- {
- std::vector<bstNode*> path1;
- std::vector<bstNode*> path2;
- bool find = false;
- find |= nodePath(pNode, value1, path1);
- find &= nodePath(pNode, value2, path2);
- bstNode* pReturn=NULL;
- if (find)
- {
- int minSize = path1.size()>path2.size()?path2.size():path1.size();
- int it1 = path1.size()-minSize;
- int it2 = path2.size()-minSize;
- for (;it1<path1.size(),it2<path2.size();it1++,it2++)
- {
- if (path1[it1]==path2[it2])
- {
- pReturn = path1[it1];
- break;
- }
- }
- }
- return pReturn;
- }
這段程式碼經歷了大概30分鐘的修改和debug,然後才逐漸穩定下來,真的很難想象如果是在面試的環境下,在紙筆之上會有如何的表現,可能真是隻有天知道了。
相關文章
- JZ-076-樹中兩個節點的最低公共祖先
- 兩個連結串列的第一個公共節點
- 008,二叉樹的下一個節點二叉樹
- 快速求完全二叉樹的節點個數二叉樹
- LeetCode-222-完全二叉樹的節點個數LeetCode二叉樹
- Java中在二叉搜尋樹中查詢節點的父節點Java
- 刷題系列 - 在給出二叉樹中兩個點,求出其最小共同父節點二叉樹
- 二叉樹任意兩個節點間的最大距離(Java,LeetCode 543二叉樹的直徑 遞迴)二叉樹JavaLeetCode遞迴
- 為什麼二叉樹中葉子節點個數等於度為2的節點個數+1二叉樹
- 【vue】在二叉樹中根據子節點找出父節點路徑Vue二叉樹
- 刷題系列 - 在二叉樹中,為每個節點關聯其右相鄰節點二叉樹
- TCP 中的兩個細節點TCP
- 劍指Offer-38-兩個連結串列的第一個公共節點
- 程式碼隨想錄——二叉樹-11.完全二叉樹的節點個數二叉樹
- 二叉搜尋樹如何刪除節點
- 刷題系列 - 在二叉樹中查詢給出節點,並返回以該節點為根的樹二叉樹
- 671. 二叉樹中第二小的節點二叉樹
- 力扣 - 劍指 Offer 52. 兩個連結串列的第一個公共節點力扣
- leetcode------給定一個二叉樹和一個值sum,判斷是否有從根節點到葉子節點的節點值之和等於sum 的路徑,LeetCode二叉樹
- 到達二叉樹目標節點的完整路徑二叉樹
- LeetCode——671. 二叉樹中第二小的節點LeetCode二叉樹
- 資料庫——查詢樹形結構某節點的所有子節點、所有父節點資料庫
- 783. 二叉搜尋樹節點最小距離
- Kotlin 鏈式儲存的二叉樹中查詢節點Kotlin二叉樹
- 樹,計算父節點的值
- 《劍指offer》之在完全二叉樹中新增子節點二叉樹
- 二叉樹父子節點下標位置關係證明二叉樹
- 填充每個節點的下一個右側節點指標指標
- Day16 | 104.二叉樹的最大深度 、111.二叉樹的最小深度 、222.完全二叉樹的節點個數二叉樹
- 每日一練(26):二叉搜尋樹的第k大節點
- [1483. 樹節點的第 K 個祖先] 【路徑】
- 過濾/篩選樹節點
- 填充每個節點的下一個右側節點指標 II指標
- 劍指offer 面試題 7 :二叉樹的下一個節點是什麼?面試題二叉樹
- 116. 填充每個節點的下一個右側節點指標指標
- JavaScript學習之DOM(節點、節點層級、節點操作)JavaScript
- LeetCode-116-填充每個節點的下一個右側節點指標LeetCode指標
- ElasticSearch兩個節點的情況下,shard是如何分配的Elasticsearch
- 劍指 Offer 54. 二叉搜尋樹的第k大節點 mark