生活随笔
收集整理的這篇文章主要介紹了
二叉树两个节点的公共节点
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
轉自: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)步之后,齊頭并進,就能解決了。
[cpp]?view plaincopy
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);?? ?????? ?????? ????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這里)。這樣的問題,網上廣為流傳著類似的代碼:
[cpp]?view plaincopy
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,我們找到了我們的答案。下面的代碼完全按照這個思路寫成
[cpp]?view plaincopy
#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,然后才逐漸穩定下來,真的很難想象如果是在面試的環境下,在紙筆之上會有如何的表現,可能真是只有天知道了。
總結
以上是生活随笔為你收集整理的二叉树两个节点的公共节点的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。