資料結構系列 - 二叉樹遍歷
1. 前言
所謂遍歷(Traversal)是指沿著某條搜尋路線,依次對樹中每個結點均做一次且僅做一次訪問。訪問結點所做的操作依賴於具體的應用問題。
2. 二叉樹遍歷的定義
二叉樹的遍歷過程其實也是將二叉樹的非線性序列轉換成一個線性序列的過程。二叉樹是一種非線性結構,通過遍歷二叉樹,按照某種規律對二叉樹中的每個結點進行訪問,且僅訪問一次,得到一個順序序列。
從二叉樹的遞迴定義可知,一棵非空的二叉樹由根結點及左、右子樹這三個基本部分組成。因此,在任一給定結點上,可以按某種次序執行三個操作:
(1)訪問結點本身(N),
(2)遍歷該結點的左子樹(L),
(3)遍歷該結點的右子樹(R)。
以上三種操作有六種執行次序:
NLR、LNR、LRN、NRL、RNL、RLN。
注意:
前三種次序與後三種次序對稱,故只討論先左後右的前三種次序。
根據訪問結點操作發生位置命名:
① NLR: 前序遍歷 (PreorderTraversal亦稱(先序遍歷))
——訪問結點的操作發生在遍歷其左右子樹之前。
② LNR: 中序遍歷 (InorderTraversal)
——訪問結點的操作發生在遍歷其左右子樹之中(間)。
③ LRN: 後序遍歷 (PostorderTraversal)
——訪問結點的操作發生在遍歷其左右子樹之後。
注意:
由於被訪問的結點必是某子樹的根,所以N(Node)、L(Left subtlee)和R(Right subtree)又可解釋為根、根的左子樹和根的右子樹。NLR、LNR和LRN分別又稱為先根遍歷、中根遍歷和後根遍歷。
3. 遍歷演算法
① 先序遍歷的遞迴演算法定義:若二叉樹非空,則依次執行如下操作:
(1) 訪問根結點;
(2) 遍歷左子樹;
(3) 遍歷右子樹。
② 中序遍歷的遞迴演算法定義:若二叉樹非空,則依次執行如下操作:
(1)遍歷左子樹;
(2)訪問根結點;
(3)遍歷右子樹。
③ 後序遍歷得遞迴演算法定義:若二叉樹非空,則依次執行如下操作:
(1)遍歷左子樹;
(2)遍歷右子樹;
(3)訪問根結點。
(1)二叉樹的先序遍歷的遞迴演算法如下:
void preorder(BTree *b) { if(b!=NULL) { printf("%d",b->data); preorder(b->lchild); preorder(b->rchild); } }
(2)二叉樹的中序遍歷的遞迴演算法如下:
void inorder(BTree*b) { if(b!=NULL) { inorder(b->lchild); printf("%d",b->data); inorder(b->rchild); } }
(3)二叉樹的後序遍歷的遞迴演算法如下:
void postorder(BTree*b) { if(b!=NULL) { postorder(b->lchild); postorder(b->rchild); printf("%d",b->data); } }
4. 遍歷二叉樹的執行蹤跡
三種遞迴遍歷演算法的搜尋路線相同(如下圖虛線所示)。
具體線路為:從根結點出發,逆時針沿著二叉樹外緣移動,對每個結點均途徑三次,最後回到根結點。
圖 遍歷二叉樹的搜尋路徑
遍歷序列
(1) 中序序列(左中右)中序遍歷二叉樹時,對結點的訪問次序為中序序列
【例】中序遍歷上圖所示的二叉樹時,得到的中序序列為:
D B A E C F
(2) 先序序列(中左右)
先序遍歷二叉樹時,對結點的訪問次序為先序序列
【例】先序遍歷上圖所示的二叉樹時,得到的先序序列為:
A B D C E F
(3) 後序序列(左右中)
後序遍歷二叉樹時,對結點的訪問次序為後序序列
【例】後序遍歷上圖所示的二叉樹時,得到的後序序列為:
D B E F C A
注意:
(1) 在搜尋路線中,若訪問結點均是第一次經過結點時進行的,則是前序遍歷;若訪問結點均是在第二次(或第三次)經過結點時進行的,則是中序遍歷(或後序遍歷)。只要將搜尋路線上所有在第一次、第二次和第三次經過的結點分別列表,即可分別得到該二叉樹的前序序列、中序序列和後序序列。
(2) 上述三種序列都是線性序列,有且僅有一個開始結點和一個終端結點,其餘結點都有且僅有一個前趨結點和一個後繼結點。為了區別於樹形結構中前趨(即雙親)結點和後繼(即孩子)結點的概念,對上述三種線性序列,要在某結點的前趨和後繼之前冠以其遍歷次序名稱。
【例】上圖所示的二叉樹中結點C,其前序前趨結點是D,前序後繼結點是E;中序前趨結點是E,中序後繼結點是F;後序前趨結點是F,後序後繼結點是A。但是就該樹的邏輯結構而言,C的前趨結點是A,後繼結點是E和F。
5. 二叉連結串列的
(1)基本思想
基於先序遍歷的構造,即以二叉樹的先序序列為輸入構造。
注意:
先序序列中必須加入虛結點以示空指標的位置。
【例】
建立上圖所示二叉樹,其輸入的先序序列是:ABD∮∮∮CE∮∮F∮∮。
(2)構造演算法
假設虛結點輸入時以空格字元表示,相應的構造演算法為:
void CreateBinTree (BinTree *T)
{ //構造二叉連結串列。T是指向根指標的指標,故修改*T就修改了實參(根指標)本身
char ch;
if((ch=getchar())=='') *T=NULL; //讀人空格,將相應指標置空
else{ //讀人非空格
*T=(BinTNode *)malloc(sizeof(BinTNode)); //生成結點
(*T)->data=ch;
CreateBinTree(&(*T)->lchild); //構造左子樹
CreateBinTree(&(*T)->rchild); //構造右子樹
}
}
注意:
呼叫該演算法時,應將待建立的二叉連結串列的根指標的地址作為實參。
【例】
設root是一根指標(即它的型別是BinTree),則呼叫CreateBinTree(&root)後root就指向了已構造好的二叉連結串列的根結點。