資料結構非順序結構之樹
樹是一種非線性的資料結構,用它能很好地描述有分支和層次特性的資料集合。樹型結構在現實世界中廣泛存在,如社會組織機構的組織關係圖就可以用樹型結構來表示。樹在計算機領域中也有廣泛應用,如在編譯系統中,用樹表示源程式的語法結構。在資料庫系統中,樹型結構是資料庫層次模型的基礎,也是各種索引和目錄的主要組織形式。在許多演算法中,常用樹型結構描述問題的求解過程、所有解的狀態和求解的對策等。在這些年的國內、國際資訊學奧賽、大學生程式設計比賽等競賽中,樹型結構成為參賽者必備的知識之一,尤其是建立在樹型結構基礎之上的搜尋演算法。
在樹型結構中,二叉樹是最常用的結構,它的分支個數確定、又可以為空、並有良好的遞迴特性,特別適宜於程式設計,因此也常常將一般樹轉換成二叉樹進行處理。
一棵樹是由n(n>0)個元素組成的有限集合,其中:
(1)每個元素稱為結點(node);
(2)有一個特定的結點,稱為根結點或樹根(root);
(3)除根結點外,其餘結點能分成m(m>=0)個互不相交的有限集合T0,T1,T2,……Tm-1。其中的每個子集又都是一棵樹,這些集合稱為這棵樹的子樹。
如下圖是一棵典型的樹:
A.樹是遞迴定義的;
B.一棵樹中至少有1個結點。這個結點就是根結點,它沒有前驅,其餘每個結點都有唯一的一個前驅結點。每個結點可以有0或多個後繼結點。因此樹雖然是非線性結構,但也是有序結構。至於前驅後繼結點是哪個,還要看樹的遍歷方法,我們將在後面討論;
C.一個結點的子樹個數,稱為這個結點的度(degree,結點1的度為3,結點3的度為0);度為0的結點稱為葉結點(樹葉leaf,如結點3、5、6、8、9);度不為0的結點稱為分支結點(如結點1、2、4、7);根以外的分支結點又稱為內部結點(結點2、4、7);樹中各結點的度的最大值稱為這棵樹的度(這棵樹的度為3)。
D.在用圖形表示的樹型結構中,對兩個用線段(稱為樹枝)連線的相關聯的結點,稱上端結點為下端結點的父結點,稱下端結點為上端結點的子結點。稱同一個父結點的多個子結點為兄弟結點。如結點1是結點2、3、4的父結點,結點 2、3、4是結點1的子結點,它們又是兄弟結點,同時結點2又是結點5、6的父結點。稱從根結點到某個子結點所經過的所有結點為這個子結點的祖先。如結點1、4、7是結點8的祖先。稱以某個結點為根的子樹中的任一結點都是該結點的子孫。如結點7、8、9都是結點4的子孫。
E.定義一棵樹的根結點的層次(level)為1,其它結點的層次等於它的父結點層次加1。如結點2、3、4的層次為1,結點5、6、7的層次為2,結點8、9的層次為3。一棵樹中所有的結點的層次的最大值稱為樹的深度(depth)。如這棵樹的深度為3。
F.對於樹中任意兩個不同的結點,如果從一個結點出發,自上而下沿著樹中連著結點的線段能到達另一結點,稱它們之間存在著一條路徑。可用路徑所經過的結點序列表示路徑,路徑的長度等於路徑上的結點個數減1。如上圖中,結點1和結點8之間存在著一條路徑,並可用(1、4、7、8)表示這條路徑,該條路徑的長度為3。注意,不同子樹上的結點之間不存在路徑,從根結點出發,到樹中的其餘結點一定存在著一條路徑。
G.森林(forest)是m(m>=0)棵互不相交的樹的集合。
方法1:陣列,稱為“父親表示法”。
const int m = 10; //樹的結點數
struct node
{
int data, parent; //資料域,指標域
};
node tree[m];
優缺點:利用了樹中除根結點外每個結點都有唯一的父結點這個性質。很容易找到樹根,但找孩子時需要遍歷整個線性表。
方法2:樹型單鏈表結構,稱為“孩子表示法”。每個結點包括一個數據域和一個指標域(指向若干子結點)。稱為“孩子表示法”。假設樹的度為10,樹的結點僅存放字元,則這棵樹的資料結構定義如下:
const int m = 10; //樹的度
typedef struct node;
typedef node *tree;
struct node
{
char data; //資料域
tree child[m]; //指標域,指向若干孩子結點
};
tree t;
缺陷:只能從根(父)結點遍歷到子結點,不能從某個子結點返回到它的父結點。但程式中確實需要從某個結點返回到它的父結點時,就需要在結點中多定義一個指標變數存放其父結點的資訊。這種結構又叫帶逆序的樹型結構。
方法3:
const int m = 10; //樹的度
typedef struct node;
typedef node *tree; //宣告tree是指向node的指標型別
struct node
{
char data; //資料域
tree child[m]; //指標域,指向若干孩子結點
tree father; //指標域,指向父親結點
};
tree t;
方法4:二叉樹型表示法,稱為“孩子兄弟表示法”。也是一種雙鏈表結構,但每個結點包括一個數據域和二個指標域(一個指向該結點的第一個孩子結點,一個指向該結點的下一個兄弟結點)。稱為“孩子兄弟表示法”。假設樹的度為10,樹的結點僅存放字元,則這棵樹的資料結構定義如下:
typedef struct node;
typedef node *tree;
struct node
{
char data; //資料域
tree firstchild, next; //指標域,分別指向第一個孩子結點和下一個兄弟結點
};
tree t;
樹的遍歷
在應用樹結構解決問題時,往往要求按照某種次序獲得樹中全部結點的資訊,這種操作叫作樹的遍歷。遍歷的方法有多種,常用的有:
A、先序(根)遍歷:先訪問根結點,再從左到右按照先序思想遍歷各棵子樹。如上圖先序遍歷的結果為:125634789;
B、後序(根)遍歷:先從左到右遍歷各棵子樹,再訪問根結點。如上圖後序遍歷的結果為:562389741;
C、層次遍歷:按層次從小到大逐個訪問,同一層次按照從左到右的次序。如上圖層次遍歷的結果為:123456789;
D、葉結點遍歷:有時把所有的資料資訊都存放在葉結點中,而其餘結點都是用來表示資料之間的某種分支或層次關係,這種情況就用這種方法。如上圖按照這個思想訪問的結果為:56389;