二叉樹的遍歷(前序、中序、后序、已知前中序求后序、已知中后序求前序)


之前的一篇隨筆(二叉樹、前序遍歷、中序遍歷、后序遍歷)只對二叉樹的遍歷進行了籠統的描述,這篇隨筆重點對前、中、后序的遍歷順序進行分析

二叉樹的遍歷

二叉樹的深度優先遍歷可細分為前序遍歷、中序遍歷、后序遍歷,這三種遍歷可以用遞歸實現(本篇隨筆主要分析遞歸實現),也可使用非遞歸實現的

 

前序遍歷:根節點->左子樹->右子樹(根->左->右)

中序遍歷:左子樹->根節點->右子樹(左->根->右)

后序遍歷:左子樹->右子樹->根節點(左->右->根)

在進行已知兩種遍歷順序求另一種遍歷順序前,先看一下不同遍歷順序對應的代碼

 

前序遍歷

 1 /* 以遞歸方式 前序遍歷二叉樹 */
 2 void PreOrderTraverse(BiTree t, int level)  3 {  4     if (t == NULL)  5  {  6         return ;  7  }  8     printf("data = %c level = %d\n ", t->data, level);  9     PreOrderTraverse(t->lchild, level + 1); 10     PreOrderTraverse(t->rchild, level + 1); 11 }

 

中序遍歷

 1 /* 以遞歸方式 中序遍歷二叉樹 */
 2 void PreOrderTraverse(BiTree t, int level)  3 {  4     if (t == NULL)  5  {  6         return ;  7  }  8     PreOrderTraverse(t->lchild, level + 1);  9     printf("data = %c level = %d\n ", t->data, level); 10     PreOrderTraverse(t->rchild, level + 1); 11 }

 

后序遍歷

 1 /* 以遞歸方式 后序遍歷二叉樹 */
 2 void PreOrderTraverse(BiTree t, int level)  3 {  4     if (t == NULL)  5  {  6         return ;  7  }  8     PreOrderTraverse(t->lchild, level + 1);  9     PreOrderTraverse(t->rchild, level + 1); 10     printf("data = %c level = %d\n ", t->data, level); 11 }

 

 三種遍歷方式對應的代碼幾乎相同,只是一條語句的位置發生了變化

printf("data = %c level = %d\n ", t->data, level);

 只看文字和代碼來理解遍歷的過程是比較困難的,建議讀者親自去遍歷,為了理清遍歷的過程下面上題

 

(圖片來源:https://www.cnblogs.com/xinchrome/p/4905608.html

前序遍歷

前序的遍歷的特點,根節點->左子樹->右子樹,注意看前序的遍歷的代碼printf語句是放在兩條遞歸語句之前的,所以先訪問根節點G,打印G,然后訪問左子樹D,此時左子樹D又作為根節點,打印D,再訪問D的左子樹A

A又作為根節點,打印A,A沒有左子樹或者右子樹,函數調用結束返回到D節點(此時已經打印出來的有:GDA)D節點的左子樹已經遞歸完成,現在遞歸訪問右子樹F,F作為根節點,打印F,F有左子樹訪問左子樹E,E作為

根節點,打印E,(此時已經打印出來的有:GDAFE),E沒有左子樹和右子樹,函數遞歸結束返回F節點,F的左子樹已經遞歸完成了,但沒有右子樹,所以函數遞歸結束,返回D節點,D節點的左子樹和右子樹遞歸全部完成,

函數遞歸結束返回G節點,訪問G節點的右子樹M,M作為根節點,打印M,訪問M的左子樹H,H作為根節點,打印H,(此時已經打印出來的有:GDAFEMH)H沒有左子樹和右子樹,函數遞歸結束,返回M節點,M節點的左子樹已經

遞歸完成,訪問右子樹Z,Z作為根節點,打印Z,Z沒有左子樹和右子樹,函數遞歸結束,返回M節點,M節點的左子樹右子樹遞歸全部完成,函數遞歸結束,返回G節點,G節點的左右子樹遞歸全部完成,整個二叉樹的遍歷就結束了

(MGJ,終於打完了··)

前序遍歷結果:GDAFEMHZ

 

總結一下前序遍歷步驟

第一步:打印該節點(再三考慮還是把訪問根節點這句話去掉了)

第二步:訪問左子樹,返回到第一步(注意:返回到第一步的意思是將根節點的左子樹作為新的根節點,就好比圖中D是G的左子樹但是D也是A節點和F節點的根節點)

第三步:訪問右子樹,返回到第一步

第四步:結束遞歸,返回到上一個節點

 前序遍歷的另一種表述:

(1)訪問根節點

(2)前序遍歷左子樹

(3)前序遍歷右子樹

(在完成第2,3步的時候,也是要按照前序遍歷二叉樹的規則完成)

前序遍歷結果:GDAFEMHZ

 

中序遍歷(詳細遍歷過程就不再贅述了,(┬_┬))

中序遍歷步驟

第一步:訪問該節點左子樹

第二步:若該節點有左子樹,則返回第一步,否則打印該節點

第三步:若該節點有右子樹,則返回第一步,否則結束遞歸並返回上一節點

(按我自己理解的中序就是:先左到底,左到不能在左了就停下來並打印該節點,然后返回到該節點的上一節點,並打印該節點,然后再訪問該節點的右子樹,再左到不能再左了就停下來)

中序遍歷的另一種表述:

(1)中序遍歷左子樹

(2)訪問根節點

(3)中序遍歷右子樹

(在完成第1,3步的時候,要按照中序遍歷的規則來完成)

所以該圖的中序遍歷為:ADEFGHMZ

 

后序遍歷步驟

第一步:訪問左子樹

第二步:若該節點有左子樹,返回第一步

第三步:若該節點有右子樹,返回第一步,否則打印該節點並返回上一節點

 后序遍歷的另一種表述:

(1)后序遍歷左子樹

(2)后序遍歷右子樹

(3)訪問根節點

(在完成1,2步的時候,依然要按照后序遍歷的規則來完成)

該圖的后序遍歷為:AEFDHZMG

 (讀者如果在紙上遍歷二叉樹的時候,仍然容易將順序搞錯建議再回去看一下三種不同遍歷對應的代碼)

 

進入正題,已知兩種遍歷結果求另一種遍歷結果(其實就是重構二叉樹)

第一種:已知前序遍歷、中序遍歷求后序遍歷

前序遍歷:ABCDEF

中序遍歷:CBDAEF

在進行分析前讀者需要知道不同遍歷結果的特點

1、前序遍歷的第一元素是整個二叉樹的根節點

2、中序遍歷中根節點的左邊的元素是左子樹,根節點右邊的元素是右子樹

3、后序遍歷的最后一個元素是整個二叉樹的根節點

(如果讀者不明白上述三個特點,建議再回去看一下三種不同遍歷對應的代碼,並在紙上寫出一個簡單的二叉樹的三種不同的遍歷結果,以加深對三種不同遍歷的理解)

用上面這些特點來分析遍歷結果,

第一步:先看前序遍歷A肯定是根節點

第二步:確認了根節點,再來看中序遍歷,中序遍歷中根節點A的左邊是CBD,右邊是EF,所有可以確定二叉樹既有左子樹又有右子樹

第三步:先來分析左子樹CBD,那么CBD誰來做A的左子樹呢?這個時候不能直接用中序遍歷的特點(左->根->右)得出左子樹應該是這個樣子

因為有兩種情況都滿足中序遍歷為CBD無法直接根據中序遍歷來直接得出左子樹的結構,這個時候就要返回到前序遍歷中去

觀察前序遍歷ABCDEF,左子樹CBD在前序遍歷中的順序是BCD,意味着B是左子樹的根節點(這么說可能不太好理解,換個說法就是B是A的左子樹),得出這個結果是因為如果一個二叉樹的根節點有左子樹,那么

這個左子樹一定在前序遍歷中一定緊跟着根節點(這個是用前序遍歷的特點(根->左->右)得出的),到這里就可以確認B是左子樹的根節點

第四步:再觀察中序遍歷CBDAEF,B元素左邊是C右邊是D,說明B節點既有左子樹又有右子樹,左右子樹只有一個元素就可以直接確定了,不用再返回去觀察前序遍歷

第五步:到這里左子樹的重建就已經完成了,現在重建右子樹,因為重建右子樹的過程和左子樹的過程一模一樣,步驟就不像上面寫這么細了((┬_┬)),觀察中序遍歷右子樹為EF,再觀察前序遍歷ABCDEF中右子樹

的順序為EF,所以E為A的右子樹,再觀察中序便利中E只有右邊有F,所有F為E的右子樹,最后得到的二叉樹是這個樣子的

所有求得的后序遍歷為:CDBFEA

 

總結一下上述步驟: 先觀察前序遍歷找到根節點->觀察中序遍歷將根節點左邊歸為左子樹元素,右邊歸為右子樹元素(可能會出現只有左子樹或者右子樹的情況)->觀察前序遍歷中左\右子樹幾個元素的順序,最靠前的為左\右子樹的根節點->重復前面的步驟

第二種:已知中序遍歷、后序遍歷求前序遍歷(題還是上面這道)

中序遍歷:CBDAEF

后序遍歷為:CDBFEA

仍然是根據不同遍歷方式結果的特點來重構二叉樹,過程很相似這里就不詳細說了,后序遍歷的最后一個元素A是根節點,在中序遍歷中以根節點A作為分界將元素分為左子樹(CBD)和右子樹(EF),再觀察后序遍歷中左子樹的順序是CDB

,可以判斷出B是左子樹的根節點(因為后序遍歷是:左->右->根),再觀察中序遍歷,B元素左邊是C右邊是D,說明B節點既有左子樹又有右子樹,左右子樹只有一個元素就可以直接確定了,不用再返回去觀察后序遍歷,左子樹重建完成,

現在來看右子樹,右子樹有兩個元素EF,觀察后序遍歷E在F的后面,所以E是右子樹的根節點,然后看中序遍歷中E只有右邊一個F元素了,即F是E的右子樹,此時整個二叉樹重構完成

 

總結一下上述步驟:先觀察后序遍歷找到根節點->觀察中序遍歷將根節點左邊歸為左子樹元素,右邊歸為右子樹元素(可能會出現只有左子樹或者右子樹的情況)->觀察后序遍歷中左\右子樹幾個元素的順序,最靠后的為左\右子樹的根節點->重復前面的步驟

 

注意:已知前序遍歷、后序遍歷無法求出中序遍歷(因為由前序后序重構出來的二叉樹不止一種)

舉個栗子左圖這兩種二叉樹前序(BEFA)和后序(AFEB)一樣,但對應的中序遍歷結果不一樣(左邊的是AFEB右邊的是BEFA),所以僅靠前序后序是

重構出唯一的二叉樹

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM