樹(tree)

樹(tree)

樹(tree)型結構是一類重要的非線性結構。樹型結構反映了數據元素之間的層次關係和分支關係,很是相似於天然界中的樹。java

一、概念

樹是由一個集合以及在該集合上定義的一種關係構成的。集合中的元素稱爲樹的結點,所定義的關係稱爲父子關係。父子關係在樹的結點之間創建了一個層次結構。node

1.遞歸定義

樹是N(N>0)個結點的有限集合。算法

集合中存在惟一的一個結點,稱爲樹根(root),該結點沒有前趨結點,除根結點之外其他結點分爲M(M≥0)個互不相交的集合,其中每個集合都是一顆樹,並稱其爲根的子樹(sub tree)。數組

以下圖,爲T={R,A,B,C,D,E,F}的樹型表示法數據結構

採用子樹概念遞歸定義數爲:樹是由根節點和若干棵子樹構成的。post

2.基本術語

  • 一個結點的子樹個數稱爲該結點的度(degree)
  • 一棵樹中結點度的最大值稱爲該樹的度
  • 度爲零的結點稱爲葉子(leaf)或者終端結點
  • 度不爲零的結點稱爲分支結點或者非終端結點
  • 除根結點以外的分支結點統稱爲內部結點
  • 樹中結點的後繼結點稱爲兒子(child)或者兒子結點
  • 結點的前趨結點稱爲兒子結點的父親(parents)或者父結點
  • 同一個父親的兒子稱爲兄弟(sibling)
  • 結點的層次(level)從根開始定義,層次數爲0的結點是根結點,其子樹的根的層次數爲1。若結點在L層,其子樹的根就在L+1層。
  • 樹中結點的最大層次數稱爲樹的深度(depth)高度
  • 在樹中k+1個結點經過k條邊鏈接構成的序列{(v0,v1),(v1,v2), … ,(vk-1,vk)| k ≥0},稱爲長度爲k的路徑(path)
  • 從根到該結點路徑上的全部結點都是該結點的祖先(Ancestor)
  • 以某結點爲根的樹中的任一結點都稱爲該結點的子孫(Descendant)
  • 父親在同一層次的結點互爲堂兄弟
  • 若是將樹中結點的各子樹當作是從左至右是有次序的,則稱該樹爲有序樹(Ordered Tree);若不考慮子樹的順序則稱爲無序樹(Unordered Tree)
  • 樹中全部結點最大度數爲m的有序樹稱爲m叉樹
  • 森林(forest)是m(m ≥0 )棵互不相交的樹的集合。對樹中每一個結點而言,其子樹的集合即爲森林。樹和森林的概念相近。刪去一棵樹的根,就獲得一個森林;反之,加上一個結點做樹根,森林就變爲一棵樹。

樹中結點之間所存在的父子關係能夠用於描述樹型結構的邏輯特徵:編碼

  • 樹中任一個結點均可以有領個或者多個後繼結點,但最多隻能有一個前趨結點。
  • 樹中只有根結點無前趨,葉子結點無後繼。

樹型結構是非線性結構。.net

祖先與子孫的關係則是對父子關係的延伸,其定義了樹中結點的縱向次序。3d

有序樹的兄弟關係的延伸,定義了樹中結點的橫向次序。指針

3.元祖定義

ADT表示爲:TREE=(D,R)

其中D是具備相同特性的數據元素的集合;R是元素集合D上的關係集合。若是D中只含有一個數據元素,則R爲空集。

4.ADT定義

下面是數的抽象數據類型的定義:

ADT Tree{
數據對象D:D是具備相同性質的數據元素的集合。
數據關係R:若D=Φ則R=Φ;若D≠Φ,則R={H},H是以下二元關係:
      ①在D中存在一個惟一的稱爲根的元素root,它在H下無前驅;
      ②除root之外,D中每一個結點在H下都有且僅有一個前驅。
基本操做:
getSzie();				//返回樹的結點數。
getRoot();				//返回根結點。
getParent(x);			//返回結點x的父結點。
getFirstChild(x);			//返回結點x的第一個兒子。
getNextSibiling(x);		//返回結點x的下一個兄弟結點。
getHeight(x);			//返回以x爲根的樹的高度。
insertChild(x,child);		//將結點child爲根的子樹插入樹中,做爲x的子樹
deleteChile(x,i);			//刪除x的第i棵子樹。
proOrder(x);			//先序遍歷
postOrder(x);			//後續遍歷
levelOrder(x);			//按層遍歷
}ADT Tree

5.表示方式

樹的經常使用表示方式:樹型表示法、文氏圖表示法、凹入圖表示法以及廣義表表示法。 以下圖:

二、二叉樹(binary tree)

1.定義

每一個結點的度均不超過2的有序樹,稱爲二叉樹(binary tree)。

二叉樹的遞歸定義以下:

二叉樹或者是一棵空樹,或者是一棵由一個根結點和兩棵互不相交的分別稱爲根的左子樹和右子樹的子樹所組成的非空樹。

二叉樹中每一個結點的孩子數只能是0、1或2個,而且每一個孩子都有左右之分。

位於左邊的孩子稱爲左孩子,位於右邊的孩子稱爲右孩子;以左孩子爲根的子樹稱爲左子樹,以右孩子爲根的子樹稱爲右子樹。

二叉樹結構與通常樹結構的區別:

  • 第一:二叉樹能夠是空樹,即不包含任何結點;通常樹至少應該有一個結點。
  • 第二:二叉樹區別於度數爲2的有序樹,在二叉樹中容許某些結點只有右子樹而沒有左子樹;有序樹中,一個結點若是沒有第一子樹就不可能有第二子樹的存在。

所以二叉樹並不是是樹的特殊情形,他們是兩種不一樣的數據結構。

2.分類

每層結點都達到最大數的二叉樹稱爲滿二叉樹。

能夠對滿二叉樹的結點進行編號,約定編號從根結點起,層間自上而下,層內自左而右,逐層由1到n進行標號。

若一棵二叉樹最多隻有最下面的兩層結點的度數能夠小於2,而且最下一層上的結點都集中在該層最左邊的若干位置上,則此二叉樹稱爲徹底二叉樹。

3.性質

性質1

二叉樹第i(i≥1)層上的結點樹最多爲2^(i-1)。

性質2

高度爲k的二叉樹最多有2^k-1個結點。

性質3

對任何二叉樹T,設n0、n一、n2分別表示度數爲0、一、2的結點樹,則n0=n2+1。

由性質3能夠推導出:

當n2=0時,n0=1。即該二叉樹有一個起始結點和一個終端結點,其餘各結點有一個父親和兒子,二叉樹退化爲單鏈表。

性質4

有n個結點的徹底二叉樹的高度爲⎣log n⎦。

性質5

滿二叉樹原理 非空滿二叉樹的葉節點數等於其分支結點數加1。

性質6

一棵非空二叉樹空子樹的數目等於其結點數目加1.

4.存儲結構

二叉樹的存儲結構:順序存儲結構和連接存儲結構。

1>順序存儲結構

二叉樹的順序存儲結構是把二叉樹的全部結點按照必定的次序順序存儲到一組包含n個存儲單元的額空間中。

在二叉樹的順序存儲結構中只存儲結點的值(數據域),不存儲結點之間的邏輯關係,結點之間的邏輯關係由數組中下標的順序來體現。

二叉樹順序存儲的原則是:無論給定的二叉樹是否是徹底二叉樹,都看做徹底二叉樹,即按照徹底二叉樹的層次次序把各個結點依次存儲數組中。

如圖爲二叉樹的順序存儲結構:

在順序存儲結構中,由某個結點的存儲單元地址能夠推出其父親、左兒子、右兒子及其兄弟的地址,假設給定結點的地址爲I,則:

  • (1)若I=1,則該結點是根結點,無父親。
  • (2)若I≠1,則該結點的父親結點地址爲I/2的整數部分。
  • (3)若2×I≤n,則該結點的左二指結點地址爲2×I,不然該結點無左兒子。
  • (4)若2×I+1≤n,則該結點的右兒子結點地址爲2×I+1,不然該結點無右兒子。
  • (5)若I爲奇數(不爲1),則該結點的左兄弟爲I-1。
  • (6)若I爲偶數(不爲n),則該結點的右兄弟爲I+1。

順序存儲結構對徹底二叉樹而言,既簡單又節省存儲空間。

通常二叉樹爲了能用結點在數組中的相對位置表示結點之間的邏輯關係,也必須按徹底二叉樹的形式來存儲樹中的結點,這必然形成存儲空間的浪費。

2>連接存儲結構

通常二叉樹使用順序存儲結構會形成大量存儲空間的浪費,因此通常二叉樹的存儲結構更多的採用連接的方式。

二叉樹的連接存儲結構中每一個結點由數據域和指針域兩部分組成。

二叉樹每一個結點的指針域有兩個,一個指向左兒子,一個指向右兒子。使用此結構的二叉樹的連接存儲結構稱爲二叉鏈表。

能夠在上述結點的結構中增長一個指向結點的父結點的指針域,採用此結點結構獲得的二叉樹存儲結構稱爲三叉鏈表。

5.二叉樹的遍歷

樹的遍歷(traversal)是按照某種次序訪問樹中的全部結點,且每一個結點剛好訪問一次。

二叉樹的遍歷是以遞歸的方式進行,依遞歸的調用順序的不一樣,可分爲3種不一樣的遍歷方式:前序遍歷方式、中序遍歷方式、後序遍歷方式。

1>前序遍歷

前序遍歷(Preorder Traversal)是先遍歷根結點,再遍歷左子樹,最後才遍歷右子樹。

操做步驟以下:

  • (1)訪問根結點
  • (2)前序遍歷左子樹
  • (3)前序遍歷右子樹

2>中序遍歷

中序遍歷(Inorder Traversal)是先遍歷左子樹,再遍歷根結點,最後才遍歷右子樹。

操做步驟以下:

  • (1)中序遍歷左子樹
  • (2)訪問根結點
  • (3)中序遍歷右子樹

3>後序遍歷

後續遍歷(Postorder Traversal)是先遍歷左子樹,再遍歷右子樹,最後才遍歷根結點。

操做步驟以下:

  • (1)後續遍歷左子樹
  • (2)後續遍歷右子樹
  • (3)訪問根結點

4>層次遍歷

層次遍歷是指從二叉樹的第一層開始,從上至下逐層遍歷,在同一層中,則按從左到右的順序對結點逐個訪問。

操做步驟以下:

  • (1)初始化一個隊列
  • (2)二叉樹的根結點放入隊列
  • (3)重複步驟(4)-(7)直至隊列爲空
  • (4)從隊列中驅虎一個結點x
  • (5)訪問結點x
  • (6)若是x存在左子節點,將左子節點放入隊列
  • (7)若是x存在右子節點,將右子節點放入隊列

三、線索二叉樹

1.定義

利用空指針域存放結點的前趨結點和後繼幾點的指針信息,這種附加的指針稱爲線索。

增長了線索的二叉鏈表稱爲線索鏈表,相應的二叉樹稱爲線索二叉樹(Threaded Binary Tree)

爲了區分一個結點的指針是指向去兒子仍是指向其前趨後繼,爲每一個結點增長了兩個線索標誌域ltag和rtag,這樣線索鏈表的結點結構以下圖:

標誌域標誌的是左右指針域的功能,以下:

  • ltag=0:lchild是指向結點的左兒子的指針。
  • ltag=1:lchild是指向結點的前趨結點的左線索。
  • rtag=0:rchild是指向結點的右兒子的指針。
  • rtag=1:rchild是指向結點的後繼結點的右線索。

每一個標誌位只佔1bit。

2.遍歷

一棵二叉樹以某種方式遍歷並使其變成線索二叉樹的過程稱爲二叉樹的線索化。 以下圖:三種遍歷下的線索二叉樹

爲了討論算法方便起見,一般在二叉樹中增長一個與樹中結點相同類型的頭結點,令頭結點的信息域爲空,其lchild域指向二叉樹的根結點,當二叉樹爲空時,lchild域值爲空;其rchild域指向以某種方式遍歷二叉樹時最後訪問的結點,當二叉樹爲空時,rchild域指向該結點自己,同時令原來指向二叉樹根結點的頭指針指向該頭結點,以某種方式遍歷二叉樹時,第一個被訪問結點的左指針域和最後一個被訪問結點的右指針域的值若是是線索,也指向該頭結點。

如圖:

3.運算

1>建立中序線索二叉樹

建立線索二叉樹實質上就是遍歷一棵二叉樹,在遍歷的過程當中,檢查當前結點的指針域是否爲空,若是爲空改成指向前趨結點或者後繼結點的線索。

在堆一個二叉樹加線索時,必須申請一個頭結點,創建頭結點與二叉樹的根結點的線索,對二叉樹線索化後,還需創建最後一個結點與頭結點之間的線索。

總結起來就兩個步驟:

  • 第一:遍歷修改空指針域;
  • 第二:申請頭結點,並創建頭結點與根結點和最後一個結點的線索。

2>查找前趨結點

若是該結點的左標誌位爲1,那麼其左指針域所指向的結點即是它的前趨結點。

若是該結點的左標誌位爲0,代表該結點有左兒子,根據中序遍歷的定義,它的前趨結點是以該結點的左兒子爲根的子樹的最右結點。

3>查找後繼結點

若是該結點的右標誌位爲1,那麼其右指針域所指向的結點就是它的後繼結點。

若是該結點的右標誌位爲0,代表該結點有右兒子,根據中序遍歷的定義,它的後繼結點就是以該結點右兒子爲根的子樹的最左結點。

4>查找固定值

順序遍歷比對數據便可。

5>插入結點

插入結點可分爲兩種狀況考慮:

  • 其一:將新結點插入到二叉樹中做爲某結點的左兒子;
  • 其二:將新結點插入到二叉樹中做爲某結點的右兒子。

每種狀況又分別分爲兩種:

  • 插入當前結點的左右子節點爲空,爲空直接插入,修改當前結點的指針域,以及插入結點的指針域便可。
  • 插入當前結點的左右子節點不爲空,斷開原有的父子關係,從新創建上下三層的父子關係。

四、樹之間的轉換

1.樹轉換爲二叉樹

將一顆樹轉換爲二叉樹的步驟以下:

  • (1)樹中全部相鄰兄弟之間加一條線;
  • (2)對樹中的每一個結點,只保留它的第一個兒子結點之間的連線,刪去與其餘兒子結點之間的連線;
  • (3)以樹的根結點爲軸心,將整棵樹順時針轉動必定的角度,使之結構井井有條。

轉換過程以下圖:

在轉換事後的二叉樹中,左分支上的各結點在原來樹中是父子關係,右分支上的各結點在原來的樹中是兄弟關係。

2.二叉樹還原爲樹

樹轉換爲二叉樹這一轉換過程是可逆的,能夠依據二叉樹的根結點有無右兒子結點,將一顆二叉樹還原爲樹。

步驟以下:

  • (1)若某結點是其雙親的左兒子,則把該結點的右兒子、右兒子的右兒子……都與該結點的雙親結點用線鏈接起來;
  • (2)刪除原二叉樹中全部的雙親結點與右兒子結點的連線;
  • (3)整理由(1)、(2)兩步所獲得的樹,使之結構井井有條。

以下圖即爲還原過程:

3.森林轉換爲二叉樹

森林是若干棵樹的集合,森林亦可用二叉樹表示。

轉換步驟以下:

  • (1)將森林中的每棵樹都轉換成相應的二叉樹
  • (2)第一棵二叉樹不動,從第二棵二叉樹開始,依次將後一棵二叉樹的根結點做爲前一棵二叉樹根結點的右孩子,當全部的二叉樹連在一塊兒後,這樣所獲得的二叉樹就是由森林轉換獲得的二叉樹。

過程如圖:

五、樹的遍歷

樹的遍歷分爲兩種方式:先根遍歷和後根遍歷。

1.先根遍歷

先根遍歷定義爲:

  • (1)訪問根結點;
  • (2)按照從左到右的順序先根遍歷根結點的每一棵子樹。

2.後根遍歷

後跟遍歷的定義爲:

  • (1)按照從左到右的順序後跟遍歷根結點的每一棵子樹;
  • (2)訪問根結點。

3.總結

根據樹與二叉樹的轉換關係以及樹與二叉樹的遍歷定義能夠推出:

  • 樹的先根遍歷與其轉換的相應二叉樹的前序遍歷結果序列相同
  • 樹的後根遍歷與其轉換的相應二叉樹的中序遍歷結果序列相同

所以,樹的遍歷算法也可採用相應的二叉樹的遍歷算法實現。

六、森林的遍歷

森林的遍歷有三種方式:前序遍歷、中序遍歷和後續遍歷。

1.前序遍歷

前序遍歷的定義爲: 若森林非空,則:

  • (1)訪問森林中第一棵樹的根結點
  • (2)前序遍歷第一棵樹的根結點的子樹森林
  • (3)前序遍歷剩餘的其餘子森林

2.中序遍歷

中序遍歷的定義爲: 若森林非空,則:

  • (1)中序遍歷第一棵樹的根結點的子樹森林
  • (2)訪問森林中第一棵樹的根結點
  • (3)中序遍歷剩餘的其餘子森林。

3.後序遍歷

後續遍歷的定義爲: 若森林非空,則:

  • (1)後序遍歷第一棵樹中根結點的子樹森林;
  • (2)後序遍歷除去第一棵樹後剩餘的樹構成的森林;
  • (3)訪問森林中第一棵樹的根結點。

4.總結

根據森林與二叉樹的轉換關係以及森林和二叉樹的遍歷定義能夠推出:

  • 森林前序遍歷、中序遍歷和後序遍歷分別與所轉換的二叉樹的前序遍歷、中序遍歷和後序遍歷的結果序列相同。

七、樹的存儲結構

1.雙親鏈表表示法

以一組連續的存儲單元來存放樹中的結點,每一個結點有兩個域:一個是數據域,用來存放結點的信息,另外一個是雙親域,用來存放雙親的位置。 結構如圖所示:

2.孩子鏈表表示法

將一個結點全部孩子連接成一個單鏈表形,而樹中有若干個結點,則由若干個單鏈表,每一個單鏈表有一個表頭結點,全部表頭結點用一個數組來描述。 結構如圖:

3.雙親孩子鏈表表示法

如圖所示:

4.孩子兄弟鏈表表示法

相似二叉鏈表,但第一根鏈指向第一個孩子,第二根鏈指向下一個兄弟。 結構如圖:

八、哈夫曼樹

哈夫曼(Huffman)樹又稱最優樹,能夠用來構造最優編碼,用於信息傳輸、數據壓縮等方面,是一類有着普遍應用的二叉樹。

1.概念

  • 在一棵樹中,從一個結點往下能夠達到的孩子或子孫結點之間的通路稱爲路徑。
  • 通路中分支的數據稱爲路徑長度。
  • 若將樹中結點賦給一個有着某種含義的數值,則這個數值稱爲該結點的權。
  • 結點的帶權路徑長度爲:從根結點到該結點之間的路徑長度與該結點的權的乘積。
  • 樹的帶權路徑長度規定爲全部葉子結點的帶權路徑長度之和,記爲WPL。
  • 按必定規則構造一棵二叉樹,使帶權路徑長度達到最小,稱這樣的二叉樹爲最優二叉樹,也稱哈夫曼樹。

2.編碼問題應用

在數據通訊中,常常須要將傳送的文字轉換成由二進制字符0、1組成的二進制串,咱們稱之爲二進制編碼。 在發送端,須要將電文中的字符轉換成二進制的0、1序列,而在接受端則要將受到的0、1序列轉換成對應的字符序列。

哈弗曼樹可用於構造使電文的編碼總長最短的編碼方案:

規定哈弗曼樹中的左分支表明0,右分支表明1,則從根結點到每一個葉節點所通過的路徑分支組成的0或1序列便爲該結點對應字符的編碼,稱爲哈夫曼編碼。

在哈夫曼編碼樹中,樹的帶權路徑長度的含義是各個字符的碼長與其出現次數的乘積和,也就是電文的代碼總長。

由於哈夫曼算法構造的是帶權路徑長度最小的二叉樹,因此採用哈夫曼樹構造的編碼是一種能使電文代碼總長最短的不等長編碼。

實現哈夫曼樹的算法可分爲兩大部分,構造哈夫曼樹和在哈夫曼樹上求葉結點的編碼。

在構造哈夫曼樹時,能夠設置一個結構數組huff_node保存哈夫曼樹中各結點的信息,根據二叉樹的性質可知,huff_node數組的大小可設置爲2n-1。 數組元素的結構以下:

  • weight域:保存結點的權值。
  • lchild、rchild域:分別保存該結點的左右孩子結點在數組中的序號,從而創建關係。
  • flag:標誌域。當flag=0時,表示該結點未加入樹中;當flag=1時,表示該結點已加入樹中。

上一篇:數組和廣義表

下一篇:圖(graph)

相關文章
相關標籤/搜索