一、樹的定義
(一)概述
樹是一種非常適合查找的數據結構,掌握樹這種數據結構非常重要,樹這種數據結構需要使用遞歸算法,所以在學習樹之前,瞭解遞歸的思想非常重要,這裏不展開詳述,只提一點:理解遞歸時,切忌陷入程序遞歸的內部去思考遞歸算法,記住要從遞歸思維的本質(複雜問題簡單化)出發去理解遞歸算法,千萬不要去通過試圖解析程序執行的每一個步驟來理解遞歸(解析程序的執行是指給函數一個真實值,然後自己一步步去推出結果,這樣的思考方式是錯誤的!)。
所謂查找就是根據某個給定關鍵字K,從集合R中找出關鍵字與K相同的記錄,查找又分爲靜態查找和動態查找:
靜態查找
集合中記錄是固定的,即沒有插入和刪除操作,只有查找動態查找
集合中記錄是動態變化的,除查找外,還有插入和刪除操作
(二)定義
樹:n(n≥0)個結點構成的有限集合。
當n=0時,稱爲空樹。
對於任何一顆非空樹,其子樹(SubTree)之間互不相交,每棵樹有且只有一個最頂層的結點被稱作根(root)。
二、一些基本術語
邊(edge):
結點與結點之間是通過一條有向的邊(edge)所連接的,一顆結點爲n的樹有n-1條邊,因爲除去根結點沒有邊,其餘的結點都有一條邊;結點的度(degree):
結點的子樹個數,比如上圖B的度爲3,C的度爲1,E的度爲0;樹的度:
樹的所有結點中最大的度數;葉結點(leaf):
度爲0的結點;父結點(parent):
有子樹的結點是其子樹的根結點的父結點(雙親);子結點(child):
若A結點是B結點的父結點,則稱B結點是A結點的子結點;子結點也稱孩子結點;兄弟結點(sibling):
具有同一父結點的各結點之間彼此爲兄弟結點;路徑和路徑長度:
從結點n1到結點nk的路徑爲一個結點序列n1,n2,…,nk,ni是ni+1的父結點,路徑所包含的邊的個數爲路徑的長度;祖先結點(ancestor):
沿樹根到某一結點路徑上的所有結點都是這個結點的祖先結點;子孫結點(ancestor):
某一結點的子樹中的所有結點是這個結點的子孫;結點的層次(level):
結點的層次是從根開始定義的,規定根結點在1層,其他任一結點的層數是其父結點的層數加1(又是遞歸);結點的高度(《數據結構與算法分析——C語言描述》定義):
對任意結點ni,ni的深度爲從根到ni的唯一路徑的長,ni的高度爲從ni到葉結點的最長路徑長,因此,任何葉結點的高度爲0;樹的深度(depth):
樹中結點的最大層次稱爲樹的深度(depth)或高度,注意,不同的教材對這兩個名詞的定義不一樣,《數據結構與算法分析——C語言描述》裏爲這裏定義的高度-1;森林(forest):
互不相交的樹的集合,對數中的每個結點而言,其子樹的集合即爲森林。
注意,國內關於樹的深度(或高度)的定義和國外的不一樣,這裏採用的是國內的定義方式,國外的定義可以參考《數據結構與算法分析——C語言描述》這本書。
三、樹的表示形式
1、倒懸樹
即上圖表示方式。
2、嵌套集合
上圖(a)。
3、廣義表形式
上圖(b)。
4、凹入法表示形式
上圖(c)。
四、樹的存儲結構
樹的存儲結構有兩種:順序存儲(順序存儲指使用一塊地址連續的空間,高級語言裏一般使用數組)和鏈式存儲,注意根據樹的表示方法合理選擇即可。
五、樹的表示方法
1、雙親表示法
實現:定義一個結構數組存放樹的結點,每個結點含兩個域,可以按層存儲,
- 數據域:存放結點本身的信息;
- 雙親域:指示本結點的雙親結點在數組中的位置。
特點:方便尋找雙親,但是找子結點很困難。
/*定義結點*/
typedef struct node{
element data;
int parent;
}TreeNode;
/*定義樹*/
TreeNode tree[M];
2、孩子表示法
實現:使用了多重鏈表
/*孩子結點*/
typedef struct childNode{
int child;//該結點在表頭數組中的下標
struct childNode* next;//指向下一個孩子結點
};
/*表頭結點*/
typedef struct headNode{
element data;//數據域
struct childNode* fc;//指向第一個孩子結點
};
headNode t[M];//t[0]不用
還有一種帶雙親的孩子鏈表,如下圖所示:
3、孩子兄弟表示法(二叉樹表示法)
實現:用二叉鏈表作爲樹的存儲結構,鏈表中每個結點的兩個指針域分別指向其第一個孩子結點和下一個兄弟結點。
特點:操作容易,破壞了樹的層次。
/*兒子兄弟表示法*/
typedef struct node{
element data;//數據域
struct node* firstChild;//第一個兒子結點
struct node* siblingChild;//其兄弟結點
};