(轉自http://blog.csdn.net/x1247600186/article/details/24670775)
說到存儲結構,我們就會想到常用的兩種存儲方式:順序存儲和鏈式存儲兩種。
先來看看順序存儲,用一段地址連續的存儲單元依次存儲線性表中數據元素,這對於線性表來說是很自然的,但是對於樹這種一對多的結構而言是否適合呢?
樹中某個結點的孩子可以有多個,這就意味着,無論用哪種順序將樹中所有的結點存儲到數組中,結點的存儲位置都無法直接反映邏輯關系,試想一下,數據元素挨個存儲,那么誰是誰的雙親,誰是誰的孩子呢?所以簡單的順序存儲是不能滿足樹的實現要求的。
不過可以充分利用順序存儲和鏈式存儲結構的特點,完全可以實現對樹的存儲結構的表示。
下面介紹三種不同的樹的表示法:雙親表示法,、孩子表示法,、孩子兄弟表示法。
1、雙親表示法:
我們假設以一組連續空間存儲樹的結點,同時在每個結點中,附設一個指示器指向其雙親結點到鏈表中的位置。也就是說每個結點除了知道自己之外還需要知道它的雙親在哪里。
它的結構特點是如圖所示:
以下是我們的雙親表示法的結構定義代碼:
/*樹的雙親表示法結點結構定義 */ #define MAXSIZE 100 typedef int ElemType; //樹結點的數據類型,暫定為整形 typedef struct PTNode //結點結構 { ElemType data; //結點數據 int parent; //雙親位置 }PTNode; typedef struct { PTNode nodes[MAXSIZE]; //結點數組 int r,n; //根的位置和結點數 }PTree;
2、孩子表示法
換一種不同的考慮方法。由於每個結點可能有多棵子樹,可以考慮使用多重鏈表,即每個結點有多個指針域,其中每個指針指向一棵子樹的根結點,我們把這種方法叫做多重鏈表表示法。不過樹的每個結點的度,也就是它的孩子個數是不同的。所以可以設計兩種方案來解決。
方案一:
一種是指針域的個數就等於樹的度(樹的度是樹的各個結點度的最大值)
其結構如圖所示:
不過這種結構由於每個結點的孩子數目不同,當差異較大時,很多結點的指針域就都為空,顯然是浪費空間的,不過若樹的各結點度相差很小時,那就意味着開辟的空間都被利用了,這時這種缺點反而變成了優點。
方案二:
第二種方案是每個結點指針域的個數等於該結點的度,我們專門取一個位置來存儲結點指針域的個數。
其結構如圖所示:
這種方法克服了浪費空間的缺點,對空間的利用率是很高了,但是由於各個結點的鏈表是不相同的結構,加上要維護結點的度的數值,在運算上就會帶來時間上的損耗。
能否有更好的方法呢,既可以減少空指針的浪費,又能是結點結構相同。
說到這大家肯定就知道是有的麥,那就是孩子表示法。
具體辦法是,把每個結點的孩子排列起來,以單鏈表做存儲結構,則n個結點有n個孩子鏈表,如果是葉子結點則此單鏈表為空。然后n個頭指針有組成一個線性表,采用順序存儲結構,存放進入一個一維數組中。
為此,設計兩種結點結構,
一個是孩子鏈表的孩子結點,如下所示:
其中child是數據域,用來存儲某個結點在表頭數組中的下標。next是指針域,用來存儲指向某結點的下一個孩子結點的指針。
另一個是表頭結點,如下所示:
其中data是數據域,存儲某結點的數據信息。firstchild是頭指針域,存儲該結點的孩子鏈表的頭指針。
以下是孩子表示法的結構定義代碼:
/*樹的孩子表示法結點結構定義 */ #define MAXSIZE 100 typedef int ElemType; //樹結點的數據類型,暫定為整形 typedef struct CTNode //孩子結點 { int child; struct CTNode *next; }*ChildPtr; typedef struct //表頭結構 { ElemType data; ChildPtr firstchild; }CTBox; typedef struct //樹結構 { CTBox nodes[MAXSIZE]; //結點數組 int r,n; //根結點的位置和結點數 }CTree;
3、孩子兄弟表示法
我們發現,任意一顆樹,它的結點的第一個孩子如果存在就是的,它的右兄弟如果存在也是唯一的。因此,我們設置兩個指針,分別指向該結點的第一個孩子和此結點的右兄弟。
其結點結構如圖所示:
以下是孩子兄弟表示法的結構定義代碼:
/*樹的孩子兄弟表示法結構定義 */ typedef struct CSNode { ElemType data; struct CSNode *firstchild, *rightsib; }CSNode, *CSTree;