抽象數據類型:
樹的存儲結構:
利用順序存儲和鏈式存儲的特點,完全可以實現對樹的存儲結構的表示。介紹三種不同的表示法:雙親表示法、孩子表示法、孩子兄弟表示法。
1.雙親表示法
我們假設以一段連續空間存儲樹的結點,同時在每個結點中,附設一個指示器指示其雙親結點到鏈表中的位置。也就是說,每個結點除了直到自己是誰外,還要直到自己的雙親在哪裏。如圖:
其中data就是數據域,存儲結點的信息。而parent是指針域,存儲該結點的雙親在數組中的下標。
雙親表示法的結點結構定義代碼:
//樹的雙親表示法結點結構定義
#define MAX_TREE_SIZE 100
typedef int TElemType; //樹結點的數據類型,目前暫定整形
typedef struct PTNode{ //結點結構
TElemType data; //結點數據域
int parent; //雙親位置
}PTNode;
typedef struct{ //樹結構
PTNode nodes[MAX_TREE_SIZE]; //結點數組
int r,n; //根節點的位置和結點數
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
有了這樣的結構定義,我們就可以實現雙親定義法了。由於跟結點沒有雙親,所以我們約定根結點的位置域設置爲-1,這就意味着,我們所有結點都直到他雙親的位置。如圖的樹結構和雙親表示法的圖表。
我們可以通過上面快速的找到結點的雙親。但是我們要知道結點的孩子怎麼辦?那就只有遍歷整個結構了。
我們能否改進一下?當然可以,我們可以給結點增加孩子域。
2.孩子表示法
換一種完全不同的想法。由於樹中每個結點可能有很多的子樹,可以考慮用多重鏈表,即每一個結點有多個指針域,其中每個指針指向一個子樹的根節點,我們把這種方法叫做多重鏈表表示法。不過,樹的每個結點的度,也就是它孩子個數是不同的。所以可以設計兩種方案來解決。
方案一
指針域的個數等於樹的度。(樹的度是樹各個結點度的最大值)。(結點擁有的子樹稱爲結點的度)。
如圖,data就是數據域,child1····是指針域,用來指向該結點的孩子結點。
就像6-4-1的樹一樣,我們用這個方法來實現。如圖。
我們可以看到。^這個符號就是代表當前這個指針域並沒有用到。這樣如果樹的各結點的度差距過大的話,顯然非常浪費空間。那我們爲什麼不按需分配空間呢?這樣就有第二種方案了
方案二
每個結點指針域的個數等於該結點的度,我們專門來取一個位置來存儲結點指針域的個數。如圖。
如圖,data就是數據域。degree是度域,也就是存在該結點的孩子結點數。child1····是指針域,用來指向該結點的孩子結點。
那麼對應的樹圖就應該是這樣。
顯然,方案二克服了浪費空間的缺點,但是由於各個結點的鏈表結構是不相同的,在加上要維護結點的度的數值,在運算上顯然有損耗。
能否有更好的方法?既可以減少浪費,又能使結點結構相同。
我們把每一個結點放入順序存儲結構中是合理的,但是每個結點的孩子多少是不確定的,所以我們再對每個結點的孩子建立一個單鏈表來體現他們的關係。
這就是我們的孩子表示法。
具體辦法:把每個結點的孩子排列起來,以單鏈表作存儲結構,則n個結點有n個孩子鏈表,如果是葉子結點則此單鏈表爲空。然後n個頭指針有組成一個線性表,採用順序存儲結構,存進一個一維數組。如圖。
爲此,設計兩種結構,一個是孩子鏈表的孩子結點,如圖。
child是數據域,存儲某個結點在表頭數組中的下標。next是指針域,用來存儲某結點的下一孩子結點的指針。
另一個是表頭數組的表頭結點。如圖。
data是數據域,存儲某結點的數據信息。firstchild是頭指針域,存儲該節點的孩子鏈表的頭指針。
以下是孩子表示法的結構定義代碼。
//樹的孩子表示法結點結構定義
#define MAX_TREE_SIZE 100
typedef int TElemType; //樹結點的數據類型,目前暫定整形
typedef struct CTNode{ //孩子結點
int child;
struct CTNode * next;
}*ChildPtr;
typedef struct{ //表頭結構
TElemType data;
ChildPtr firstchild;
}CTBox;
typedef struct{ //樹結構
CTBox nodes[MAX_TREE_SIZE]; //結點數組
int r,n; //根節點的位置和結點數
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
我們可以改進一下。把雙親表示法和孩子表示法綜合一下,加一個雙親域。如圖。(雙親孩子表示法)
3.孩子兄弟表示法
任何一棵樹,它的結點的第一個孩子如果是唯一的,它的右兄弟如果存在也是唯一的,因此,我們設置兩個指針,分別指向該結點的第一個孩子和此結點的右兄弟。
結點結構如圖。
data是數據域,firstchild爲指針域,存儲第一個孩子結點的地址,rightsib是指針域,存儲該結點的右兄弟的地址。
結構定義代碼如下.
//樹的孩子兄弟表示法結構定義
typedef struct CSNode{
TElemType data;
struct CSNode *firstchild,*rightsib;
}CSNode,*CSTree;
- 1
- 2
- 3
- 4
- 5
對於6-4-1的樹來說,這種方法實現的示意圖如圖。
這種方法給查找某結點的某個孩子帶來了方便,只需要通過firstchild 找到此結點的左兒子,然後再通過左兒子找到二弟,一直下去,知道找到具體的孩子。當然,如果想要找到雙親,完全可以增加一個parent 指針域來解決。
參考:
大話數據結構/程傑 著.—北京:清華大學出版社,2011.6
</div>