樹的雙親表示法、孩子表示法和孩子兄弟表示法
在使用樹結構描述實際問題時,大多數不是二叉樹,更多的是普通的樹結構,在存儲之間具有普通樹結構的數據時,經常使用的方法有3種:
代碼表示:
例如,使用雙親表示法存儲圖 1(A)中的樹結構時,數組存儲結果為(B):
(A) (B)
圖 1 雙親表示法
當算法中需要在樹結構中頻繁地查找某結點的父結點時,使用雙親表示法最合適。當頻繁地訪問結點的孩子結點時,雙親表示法就很麻煩,采用孩子表示法就很簡單。
例如,使用孩子表示法存儲圖 1 (A),存儲效果如圖 2:
圖 2 孩子表示法
使用孩子表示法存儲的樹結構,正好和雙親表示法相反,適用於查找某結點的孩子結點,不適用於查找其父結點。可以將兩種表示方法合二為一,存儲效果如圖 3:
圖 3 孩子雙親表示法
圖 4 結點構成
其中孩子指針域,表示指向當前結點的第一個孩子結點,兄弟結點表示指向當前結點的下一個兄弟結點。
代碼表示:
通過孩子兄弟表示法,普通樹轉化為了二叉樹,所以孩子兄弟表示法又被稱為“二叉樹表示法”或者“二叉鏈表表示法”。
例如,用孩子兄弟表示法表示圖 1 (A)的普通樹,存儲結果為:
圖 5 二叉鏈表表示法
圖 6 森林轉化成二叉樹
如圖 6 所示,(A)中由三棵普通樹組成的森林,首先三棵普通樹采用孩子兄弟表示法各自轉化成二叉樹,如(B)所示;然后由(B)轉(C)時,將森林中第一棵樹的樹根作為轉化后的整棵二叉樹的樹根,其他數的樹根作為第一棵樹的樹根的兄弟結點,如(C)所示。
轉化成二叉樹的森林,做的最多的操作就是查找樹中的結點。在遍歷轉化后的二叉樹時,遍歷方式有先序遍歷、中序遍歷和后序遍歷。
- 雙親表示法
- 孩子表示法
- 孩子兄弟表示法
雙親表示法
取一塊連續的內存空間,在存儲每個結點的同時,各自都附加一個記錄其父結點位置的變量。
在樹結構中,除了樹根外,每個結點都只有一個父結點(又叫“雙親結點”)。
代碼表示:
#define tree_size 100 //宏定義樹中結點的最大數量 #define TElemType int //宏定義樹結構中數據類型
typedef struct PTNode
{ TElemType data; //樹中結點的數據類型 int parent; //結點的父結點在數組中的位置下標 }PTNode;
typedef struct
{ PTNode nodes[tree_size]; //存放樹中所有結點 int r, n; //根的位置下標和結點數 }PTree;

(A) (B)
圖 1 雙親表示法
孩子表示法
將樹中的每個結點的孩子結點排列成一個線性表,用鏈表存儲起來。對於含有 n 個結點的樹來說,就會有 n 個單鏈表,將 n 個單鏈表的頭指針存儲在一個線性表中,這樣的表示方法就是孩子表示法。代碼表示:如果結點沒有孩子(例如葉子結點),那么它的單鏈表為空表。
#define TElemType int #define Tree_Size 100
//孩子表示法 typedef struct CTNode
{ int child; //鏈表中每個結點存儲的不是數據本身,而是數據在數組中存儲的位置下標 struct CTNode *next; }*ChildPtr;
typedef struct
{ TElemType data; //結點的數據類型 ChildPtr firstchild; //孩子鏈表的頭指針 }CTBox;
typedef struct
{ CTBox nodes[Tree_Size]; //存儲結點的數組 int n, r; //結點數量和樹根的位置 }CTree;

圖 2 孩子表示法
使用孩子表示法存儲的樹結構,正好和雙親表示法相反,適用於查找某結點的孩子結點,不適用於查找其父結點。可以將兩種表示方法合二為一,存儲效果如圖 3:

圖 3 孩子雙親表示法
孩子兄弟表示法
使用鏈式存儲結構存儲普通樹。鏈表中每個結點由 3 部分組成:
圖 4 結點構成
代碼表示:
#define ElemType int typedef struct CSNode
{ ElemType data; struct CSNode *firstchild, *nextsibling; }CSNode, *CSTree;
通過孩子兄弟表示法,普通樹轉化為了二叉樹,所以孩子兄弟表示法又被稱為“二叉樹表示法”或者“二叉鏈表表示法”。
例如,用孩子兄弟表示法表示圖 1 (A)的普通樹,存儲結果為:

圖 5 二叉鏈表表示法
補:森林和二叉樹的相互轉化
通過孩子兄弟表示法的學習,對於任意一棵樹,都可以找到唯一的一棵二叉樹與之對應。而森林是由多棵樹組成,為了便於對森林的遍歷等操作,需要將森林中的所有樹都組合成一顆大的二叉樹,轉化步驟為:普通樹轉化成的二叉樹,其根結點都沒有右孩子,即普通樹對應的二叉樹肯定沒有右子樹。
- 首先將森林中樹各自轉化為二叉樹;
- 森林中第一棵二叉樹的樹根作為轉化后二叉樹的樹根;
- 其他樹的樹根作為第一棵樹樹根的兄弟結點,進行連接;

圖 6 森林轉化成二叉樹
如圖 6 所示,(A)中由三棵普通樹組成的森林,首先三棵普通樹采用孩子兄弟表示法各自轉化成二叉樹,如(B)所示;然后由(B)轉(C)時,將森林中第一棵樹的樹根作為轉化后的整棵二叉樹的樹根,其他數的樹根作為第一棵樹的樹根的兄弟結點,如(C)所示。
轉化成二叉樹的森林,做的最多的操作就是查找樹中的結點。在遍歷轉化后的二叉樹時,遍歷方式有先序遍歷、中序遍歷和后序遍歷。
對森林使用先序和中序遍歷的結果和對轉化后的二叉樹使用先序和中序遍歷得到的序列是一樣的,而使用后序遍歷得到的結果不同。例如圖 6(B)中森林采用中序遍歷和(C)中二叉樹采用中序遍歷得到的結果是相同的,遍歷序列都為:
B C D A F E H J I G
。
總結
樹的三種表示方法中,雙親表示法和孩子表示法在實際算法中的應用場景正好相反:雙親表示法應用於解決查找某結點的父結點,而孩子表示法應用於查找某結點的孩子結點。孩子兄弟表示法可以將普通樹轉化成二叉樹存儲,在實際操作中,可以應用二叉樹的性質來解決普通樹或者森林的問題。