資料結構系列 - 樹的轉換
1. 樹的儲存結構
通常,樹的儲存結構有三種,雙親表示法、孩子表示法和孩子兄弟表示法。
(1)雙親表示法
雙親表示法是利用一組連續的儲存單元儲存樹的每個結點,並利用一個指示器表示結點的雙親結點在樹中的位置。
樹的雙親表示法儲存表示描述如下:
#define MaxSize 200 typedef struct PNode /* 雙親表示法的結點定義 */ { DataType data; int parent; /* 指示結點的雙親 */ }PNode; typedef struct { PNode node[MaxSize]; int num; /* 結點的個數 */ }PTree;
圖 樹的雙親表示法
(2)孩子表示法
由於樹中每個結點可能有多個孩子,所以自然想到用多重連結串列儲存樹。在這種多重連結串列的每個結點中除了用於存放資料資訊的data域外,還有若干指標域,分別用於指向該結點的孩子結點。但是一棵樹中,不同結點的度數是不同的,那麼每個結點中到底需要多少個指標域呢?我們有如下二種方案:
① 定長結點
即樹中每個結點均按樹的度k來設定指標。n個結點的樹一共有n*k個指標域,而樹中只有n-1條邊,故樹中的空指標數目為kn-(n-1)=n(k-1)+1(k越大,浪費的空間越多)。
②不定長結點
即樹中每個結點按本結點的度來設定指標數,並在結點中增設一個度數域degree指出該結點包含的指標數。各結點不等長,雖然節省了空間,但是給運算帶來不便。
孩子結點表示法的型別定義如下:
//以下的DataType和MaxTreeSize由使用者定義 typedef struct CNode{//子連結串列結點 int child; //孩子結點在向量中對應的序號 struct CNode *next; }CNode; typedef struct{ DataType data; //存放樹中結點資料 CNode *firstchild;//孩子連結串列的頭指標 }PTNode; typedef struct{ PTNode nodes[MaxTreeSize]; Int n,root; //n為結點總數,root指出根在向量中的位置 }CTree; Ctree T; //T為孩子連結串列表示
注意:當結點T.nodes[i]為葉子時,其孩子連結串列為空,即T.nodes[i].firstchild=NULL。
(3)孩子兄弟表示法
孩子兄弟表示法,也成為樹的二叉連結串列表示法。孩子兄弟表示法,採用鏈式儲存結構,連結串列由一個數據域和兩個指標域組成。其中,資料域存放結點的資料資訊,一個指標域用來指示結點的第一個孩子結點,另一個指標域用來指示結點的下一個兄弟結點。
圖 孩子兄弟表示法
樹的孩子兄弟表示法的型別定義如下:
/* 孩子兄弟表示法的型別定義 */ typedef struct CSNode { DataType data; struct CSNode *firstchild, *nextsibling; /* 指向第一個孩子和下一個結點 */ }CSNode, *CSTree;
2. 樹、森林的遍歷
(1)樹的遍歷
設樹T如下圖所示,結點R是根,根的子樹從左到右依次為T 1 ,T 2 ,…,T k 。
(1)樹T的前序遍歷定義:
若樹T非空,則:
①訪問根結點R;
②依次前序遍歷根R的各子樹T 1 ,T 2 ,…,T k 。
(2)樹的後序遍歷定義:
若樹T非空,則:
①依次後序遍歷根T的各子樹T l ,T 2 ,…,T k ;
②訪問根結點R。
【例】對下面的(a)圖中的樹進行前序遍歷和後序遍歷,得到的前序序列和後序序列分別是ABCDE和BDCEA。
圖 樹和對應的二叉樹
注意:
① 前序遍歷一棵樹恰好等價於前序遍歷該樹對應的二叉樹
② 後序遍歷樹恰好等價於中序遍歷該樹對應的二叉樹。
(2)森林的遍歷
(1)前序遍歷森林
若森林非空,則:
①訪問森林中第一棵樹的根結點;
②前序遍歷第一棵樹中根結點的各子樹所構成的森林
③前序遍歷除第一棵樹外其它樹構成的森林。
(2)後序遍歷森林
若森林非空,則:
①後序遍歷森林中第一棵樹的根結點的各子樹所構成的森林;
②訪問第一棵樹的根結點;
③後序遍歷除第一棵樹外其它樹構成的森林。
注意:
① 前序遍歷森林等同於前序遍歷該森林對應的二叉樹
② 後序遍歷森林等同於中序遍歷該森林對應的二叉樹
【例】對下面(a)圖中所示的森林進行前序遍歷和後序遍歷,則得到該森林的前序序列和後序序列分別為ABCDEFICJH和BDCAIFJGHE。而(b)圖所示二叉樹的前序序列和中序序列也分別為ABCDEFICJH和BDCAIFJGHE。
圖 森林和對應的二叉樹
③ 當用二叉連結串列作樹和森林的儲存結構時,樹和森林的前序遍歷和後遍歷,可用二叉樹的前序遍歷和中序遍歷演算法來實現。
3. 樹、森林和二叉樹的轉換
樹或森林與二叉樹之間有一個自然的一一對應關係。任何一個森林或一棵樹可惟一地對應到一棵二叉樹;反之,任何一棵二叉樹也能惟一地對應到一個森林或一棵樹。
(1)樹、森林到二叉樹的轉換
* 將樹轉換為二叉樹
樹中每個結點最多隻有一個最左邊的孩子(長子)和一個右鄰的兄弟。按照這種關係很自然地就能將樹轉換成相應的二叉樹:
①在所有兄弟結點之間加一連線;
②對每個結點,除了保留與其長子的連線外,去掉該結點與其它孩子的連線。
【例】下面(a)圖所示的樹可轉換為(c)圖所示的二叉樹。
圖 樹轉化為二叉樹
注意:由於樹根沒有兄弟,故樹轉化為二叉樹後,二叉樹的根結點的右子樹必為空。
* 將一個森林轉換為二叉樹
具體方法是:
① 將森林中的每棵樹變為二叉樹
② 因為轉換所得的二叉樹的根結點的右子樹均為空,故可將各二叉樹的根結點視為兄弟從左至右連在一起,就形成了一棵二叉樹。
【例】下圖中,左邊包含三棵樹的森林可轉換為右邊的二叉樹。
圖 森林轉化為二叉樹
(2) 二叉樹到樹、森林的轉換
把二叉樹轉換到樹和森林自然的方式是:若結點x是雙親y的左孩子,則把x的右孩子,右孩子的右孩子,…,都與y用連線連起來,最後去掉所有雙親到右孩子的連線。
【例】下圖的森林就是由二叉樹轉換成的。
圖 森林轉化為二叉樹