數據結構中的樹存儲結構
圖1 樹的示例
圖 1(A) 是使用樹結構存儲的集合 {A,B,C,D,E,F,G,H,I,J,K,L,M} 的示意圖。對於數據 A 來說,和數據 B、C、D 有關系;對於數據 B 來說,和 E、F 有關系。這就是“一對多”的關系。
將具有“一對多”關系的集合中的數據元素按照圖 1(A)的形式進行存儲,整個存儲形狀在邏輯結構上看,類似於實際生活中倒着的樹(圖 1(B)倒過來),所以稱這種存儲結構為“樹型”存儲結構。
樹的結點
結點:使用樹結構存儲的每一個數據元素都被稱為“結點”。例如,圖 1(A)中,數據元素 A 就是一個結點;
父結點(雙親結點)、子結點和兄弟結點:對於圖 1(A)中的結點 A、B、C、D 來說,A 是 B、C、D 結點的父結點(也稱為“雙親結點”),而 B、C、D 都是 A 結點的子結點(也稱“孩子結點”)。對於 B、C、D 來說,它們都有相同的父結點,所以它們互為兄弟結點。
樹根結點(簡稱“根結點”):每一個非空樹都有且只有一個被稱為根的結點。圖 1(A)中,結點A就是整棵樹的根結點。
樹根的判斷依據為:如果一個結點沒有父結點,那么這個結點就是整棵樹的根結點。
葉子結點:如果結點沒有任何子結點,那么此結點稱為葉子結點(葉結點)。例如圖 1(A)中,結點 K、L、F、G、M、I、J 都是這棵樹的葉子結點。
子樹和空樹
子樹:如圖 1(A)中,整棵樹的根結點為結點 A,而如果單看結點 B、E、F、K、L 組成的部分來說,也是棵樹,而且節點 B 為這棵樹的根結點。所以稱 B、E、F、K、L 這幾個結點組成的樹為整棵樹的子樹;同樣,結點 E、K、L 構成的也是一棵子樹,根結點為 E。
注意:單個結點也是一棵樹,只不過根結點就是它本身。圖 1(A)中,結點 K、L、F 等都是樹,且都是整棵樹的子樹。
知道了子樹的概念后,樹也可以這樣定義:樹是由根結點和若干棵子樹構成的。
空樹:如果集合本身為空,那么構成的樹就被稱為空樹。空樹中沒有結點。
補充:在樹結構中,對於具有同一個根結點的各個子樹,相互之間不能有交集。例如,圖 1(A)中,除了根結點 A,其余元素又各自構成了三個子樹,根結點分別為 B、C、D,這三個子樹相互之間沒有相同的結點。如果有,就破壞了樹的結構,不能算做是一棵樹。
結點的度和層次
對於一個結點,擁有的子樹數(結點有多少分支)稱為結點的度(Degree)。例如,圖 1(A)中,根結點 A 下分出了 3 個子樹,所以,結點 A 的度為 3。
一棵樹的度是樹內各結點的度的最大值。圖 1(A)表示的樹中,各個結點的度的最大值為 3,所以,整棵樹的度的值是 3。
結點的層次:從一棵樹的樹根開始,樹根所在層為第一層,根的孩子結點所在的層為第二層,依次類推。對於圖 1(A)來說,A 結點在第一層,B、C、D 為第二層,E、F、G、H、I、J 在第三層,K、L、M 在第四層。
一棵樹的深度(高度)是樹中結點所在的最大的層次。圖 1(A)樹的深度為 4。
如果兩個結點的父結點雖不相同,但是它們的父結點處在同一層次上,那么這兩個結點互為堂兄弟。例如,圖 1(A)中,結點 G 和 E、F、H、I、J 的父結點都在第二層,所以之間為堂兄弟的關系。
有序樹和無序樹
如果樹中結點的子樹從左到右看,誰在左邊,誰在右邊,是有規定的,這棵樹稱為有序樹;反之稱為無序樹。
在有序樹中,一個結點最左邊的子樹稱為"第一個孩子",最右邊的稱為"最后一個孩子"。
拿圖 1(A)來說,如果是其本身是一棵有序樹,則以結點 B 為根結點的子樹為整棵樹的第一個孩子,以結點 D 為根結點的子樹為整棵樹的最后一個孩子。
森林
由 m(m >= 0)個互不相交的樹組成的集合被稱為森林。圖 1(A)中,分別以 B、C、D 為根結點的三棵子樹就可以稱為森林。
前面講到,樹可以理解為是由根結點和若干子樹構成的,而這若干子樹本身是一個森林,所以,樹還可以理解為是由根結點和森林組成的。用一個式子表示為:
Tree =(root,F)
其中,root 表示樹的根結點,F 表示由 m(m >= 0)棵樹組成的森林。
樹的表示方法
除了圖 1(A)表示樹的方法外,還有其他表示方法:
圖2 樹的表示形式
圖 2(A)是以嵌套的集合的形式表示的(集合之間絕不能相交,即圖中任意兩個圈不能相交)。
圖 2(B)使用的是凹入表示法(了解即可),表示方式是:最長條為根結點,相同長度的表示在同一層次。例如 B、C、D 長度相同,都為 A 的子結點,E 和 F 長度相同,為 B 的子結點,K 和 L 長度相同,為 E 的子結點,依此類推。
最常用的表示方法是使用廣義表的方式。圖 1(A)用廣義表表示為:
(A , ( B ( E ( K , L ) , F ) , C ( G ) , D ( H ( M ) , I , J ) ) )
總結
樹型存儲結構類似於家族的族譜,各個結點之間也同樣可能具有父子、兄弟、表兄弟的關系。本節中,要重點理解樹的根結點和子樹的定義,同時要會計算樹中各個結點的度和層次,以及樹的深度。
什么是廣義表、廣義表及定義詳解
前面講過,數組即可以存儲不可再分的數據元素(如數字 5、字符 'a'),也可以繼續存儲數組(即 n 維數組)。
但需要注意的是,以上兩種數據存儲形式絕不會出現在同一個數組中。例如,我們可以創建一個整形數組去存儲 {1,2,3},我們也可以創建一個二維整形數組去存儲 {{1,2,3},{4,5,6}},但數組不適合用來存儲類似 {1,{1,2,3}} 這樣的數據。
有人可能會說,創建一個二維數組來存儲{1,{1,2,3}}。在存儲上確實可以實現,但無疑會造成存儲空間的浪費。
對於存儲 {1,{1,2,3}} 這樣的數據,更適合用廣義表結構來存儲。
什么是廣義表
廣義表,又稱列表,也是一種線性存儲結構。
同數組類似,廣義表中既可以存儲不可再分的元素,也可以存儲廣義表,記作:
LS = (a1,a2,…,an)
其中,LS 代表廣義表的名稱,an 表示廣義表存儲的數據。廣義表中每個 ai 既可以代表單個元素,也可以代表另一個廣義表。
原子和子表
通常,廣義表中存儲的單個元素稱為 "原子",而存儲的廣義表稱為 "子表"。
例如創建一個廣義表 LS = {1,{1,2,3}},我們可以這樣解釋此廣義表的構成:廣義表 LS 存儲了一個原子 1 和子表 {1,2,3}。
以下是廣義表存儲數據的一些常用形式:
- A = ():A 表示一個廣義表,只不過表是空的。
- B = (e):廣義表 B 中只有一個原子 e。
- C = (a,(b,c,d)) :廣義表 C 中有兩個元素,原子 a 和子表 (b,c,d)。
- D = (A,B,C):廣義表 D 中存有 3 個子表,分別是A、B和C。這種表示方式等同於 D = ((),(e),(b,c,d)) 。
- E = (a,E):廣義表 E 中有兩個元素,原子 a 和它本身。這是一個遞歸廣義表,等同於:E = (a,(a,(a,…)))。
注意,A = () 和 A = (()) 是不一樣的。前者是空表,而后者是包含一個子表的廣義表,只不過這個子表是空表。
廣義表的表頭和表尾
當廣義表不是空表時,稱第一個數據(原子或子表)為"表頭",剩下的數據構成的新廣義表為"表尾"。
強調一下,除非廣義表為空表,否則廣義表一定具有表頭和表尾,且廣義表的表尾一定是一個廣義表。
例如在廣義表中 LS={1,{1,2,3},5} 中,表頭為原子 1,表尾為子表 {1,2,3} 和原子 5 構成的廣義表,即 {{1,2,3},5}。
再比如,在廣義表 LS = {1} 中,表頭為原子 1 ,但由於廣義表中無表尾元素,因此該表的表尾是一個空表,用 {} 表示。
強調一下,除非廣義表為空表,否則廣義表一定具有表頭和表尾,且廣義表的表尾一定是一個廣義表。