一、樹的存儲結構
1、雙親存儲表示法
一般採用順序存儲結構實現。用一組地址連續的存儲單元來存放樹的結點,每個結點有兩個域:
data域-----存放結點的信息;
parent域-----存放該結點雙親結點的位置。
特點:求結點的雙親很容易,但求結點的孩子需要遍歷整個向量。
存儲結構描述爲:
#define MaxTreeSize 100 //定義數組空間的大小
typedef char DataType; //定義數據類型
typedef struct
{ DataType data; //結點數據
int parent; //雙親指針,指示結點的雙親在數組中的位置
} PTreeNode;
typedef struct
{
PTreeNode nodes[MaxTreeSize];
int n; //結點總數
} PTree;
PTree T; //T是雙親鏈表
2、孩子鏈表表示法
這是樹的鏈式存儲結構。每個結點的孩子用單鏈表存儲,稱爲孩子鏈表。
n個結點可以有n個孩子鏈表(葉結點的孩子鏈表爲空表)。
n個孩子鏈表的頭指針用一個向量表示。
它的特點是:與雙親相反,求孩子易,求雙親難。
如圖所示:
存儲結構描述爲:
typedef structCTNode
{ int child; //孩子鏈表結點
struct CTNode *next;
}*ChildPtr;
typedefstruct //孩子鏈表頭結點
{ ElemType data; //結點的數據元素
ChildPtrfirstchild; //孩子鏈表頭指針
}CTBox;
typedef struct
{ CTBoxnodes[MaxTreeSize];
int n, r, //數的結點數和根結點的位置
} CTree;
3、孩子兄弟鏈表表示法
孩子兄弟鏈表表示法也是樹的一種鏈式存儲結構。用二叉鏈表作爲樹的存儲結構,每個結點的左鏈域指向該結點的第一個孩子,右鏈域指向下一個兄弟結點。
由於結點中的兩個指針指示的分別爲“孩子”和“兄弟”,故稱爲“孩子-兄弟鏈表”。這種結構也稱爲二叉鏈表。
特點:雙親只管長子,長子連接兄弟。
樹的孩子兄弟鏈表的存儲結構描述爲:
typedef struct CSNode
{
ElemTypedata;
structCSNode *firstchild, *nextsibling;
} CSNode,*CSTree;
孩子兄弟存儲結構的最大優點是可以方便地實現樹和二叉樹的相互轉換和樹的各種操作。但是,孩子兄弟存儲結構的缺點也是查找當前結點的雙親結點比較麻煩,需要從樹根結點開始逐個結點比較查找。
二、森林和二叉樹的轉換
樹與二叉樹均可用二叉鏈表作爲存儲結構,因此給定一棵樹,用二叉鏈表存儲,可唯一對應一棵二叉樹,反之亦然。
如圖所示:
將一棵樹轉化爲等價的二叉樹方法如下:
(1) 在樹中各兄弟(堂兄弟除外)之間加一根連線。
(2) 對於任一結點,只保留它與最左孩子的連線外,刪去它與其餘孩子之間的連線。
(3) 以樹根爲軸心,將整棵樹按順時鐘方向旋轉約45°。
特點:根無右子樹
樹和森林都可轉換成二叉樹,但樹轉換成二叉樹後根結點無右分支,而森林轉換後的二叉樹,其根結點有右分支。
將森林轉化爲二叉樹方法如下:
(1) 將森林中的每一棵樹轉換成等價的二叉樹。
(2) 保留第一棵二叉樹,自第二棵二叉樹始,依次將後一棵二叉樹的根結點作爲前一棵二叉樹根結點的右孩子,當所有的二叉樹依此相連後,所得到的二叉樹就是由森林轉化成的二叉樹。
(3) 以樹根爲軸心,將整棵樹按順時鐘方向旋轉約45°。
三、森林的遍歷
森林的深度優先遍歷通常也有兩種方式:前序遍歷和後序遍歷。
(1) 前序遍歷森林
若森林非空,則:
訪問森林中第一棵樹的根結點;
前序遍歷第一棵樹中根結點的各子樹所構成的森林
前序遍歷去掉第一棵樹外其它樹構成的森林。
(2) 後序遍歷森林
若森林非空,則:
後序遍歷森林中第一棵樹中根結點的各子樹所構成的森林;
訪問第一棵樹的根結點;
後序遍歷去掉第一棵樹外其它樹構成的森林。
當用二叉鏈表作爲樹和森林的存儲結構時,樹和森林的前序遍歷和後序遍歷可用二叉樹的前序遍歷和中序遍歷算法來實現。