樹(tree)型結構是一類重要的非線性結構。樹型結構反映了數據元素之間的層次關係和分支關係,很是相似於天然界中的樹。java
樹是由一個集合以及在該集合上定義的一種關係構成的。集合中的元素稱爲樹的結點,所定義的關係稱爲父子關係。父子關係在樹的結點之間創建了一個層次結構。node
樹是N(N>0)個結點的有限集合。算法
集合中存在惟一的一個結點,稱爲樹根(root),該結點沒有前趨結點,除根結點之外其他結點分爲M(M≥0)個互不相交的集合,其中每個集合都是一顆樹,並稱其爲根的子樹(sub tree)。數組
以下圖,爲T={R,A,B,C,D,E,F}的樹型表示法數據結構
採用子樹概念遞歸定義數爲:樹是由根節點和若干棵子樹構成的。post
樹中結點之間所存在的父子關係能夠用於描述樹型結構的邏輯特徵:編碼
樹型結構是非線性結構。.net
祖先與子孫的關係則是對父子關係的延伸,其定義了樹中結點的縱向次序。3d
有序樹的兄弟關係的延伸,定義了樹中結點的橫向次序。指針
ADT表示爲:TREE=(D,R)
其中D是具備相同特性的數據元素的集合;R是元素集合D上的關係集合。若是D中只含有一個數據元素,則R爲空集。
下面是數的抽象數據類型的定義:
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
樹的經常使用表示方式:樹型表示法、文氏圖表示法、凹入圖表示法以及廣義表表示法。 以下圖:
每一個結點的度均不超過2的有序樹,稱爲二叉樹(binary tree)。
二叉樹的遞歸定義以下:
二叉樹或者是一棵空樹,或者是一棵由一個根結點和兩棵互不相交的分別稱爲根的左子樹和右子樹的子樹所組成的非空樹。
二叉樹中每一個結點的孩子數只能是0、1或2個,而且每一個孩子都有左右之分。
位於左邊的孩子稱爲左孩子,位於右邊的孩子稱爲右孩子;以左孩子爲根的子樹稱爲左子樹,以右孩子爲根的子樹稱爲右子樹。
二叉樹結構與通常樹結構的區別:
所以二叉樹並不是是樹的特殊情形,他們是兩種不一樣的數據結構。
每層結點都達到最大數的二叉樹稱爲滿二叉樹。
能夠對滿二叉樹的結點進行編號,約定編號從根結點起,層間自上而下,層內自左而右,逐層由1到n進行標號。
若一棵二叉樹最多隻有最下面的兩層結點的度數能夠小於2,而且最下一層上的結點都集中在該層最左邊的若干位置上,則此二叉樹稱爲徹底二叉樹。
二叉樹第i(i≥1)層上的結點樹最多爲2^(i-1)。
高度爲k的二叉樹最多有2^k-1個結點。
對任何二叉樹T,設n0、n一、n2分別表示度數爲0、一、2的結點樹,則n0=n2+1。
由性質3能夠推導出:
當n2=0時,n0=1。即該二叉樹有一個起始結點和一個終端結點,其餘各結點有一個父親和兒子,二叉樹退化爲單鏈表。
有n個結點的徹底二叉樹的高度爲⎣log n⎦。
滿二叉樹原理 非空滿二叉樹的葉節點數等於其分支結點數加1。
一棵非空二叉樹空子樹的數目等於其結點數目加1.
二叉樹的存儲結構:順序存儲結構和連接存儲結構。
二叉樹的順序存儲結構是把二叉樹的全部結點按照必定的次序順序存儲到一組包含n個存儲單元的額空間中。
在二叉樹的順序存儲結構中只存儲結點的值(數據域),不存儲結點之間的邏輯關係,結點之間的邏輯關係由數組中下標的順序來體現。
二叉樹順序存儲的原則是:無論給定的二叉樹是否是徹底二叉樹,都看做徹底二叉樹,即按照徹底二叉樹的層次次序把各個結點依次存儲數組中。
如圖爲二叉樹的順序存儲結構:
在順序存儲結構中,由某個結點的存儲單元地址能夠推出其父親、左兒子、右兒子及其兄弟的地址,假設給定結點的地址爲I,則:
順序存儲結構對徹底二叉樹而言,既簡單又節省存儲空間。
通常二叉樹爲了能用結點在數組中的相對位置表示結點之間的邏輯關係,也必須按徹底二叉樹的形式來存儲樹中的結點,這必然形成存儲空間的浪費。
通常二叉樹使用順序存儲結構會形成大量存儲空間的浪費,因此通常二叉樹的存儲結構更多的採用連接的方式。
二叉樹的連接存儲結構中每一個結點由數據域和指針域兩部分組成。
二叉樹每一個結點的指針域有兩個,一個指向左兒子,一個指向右兒子。使用此結構的二叉樹的連接存儲結構稱爲二叉鏈表。
能夠在上述結點的結構中增長一個指向結點的父結點的指針域,採用此結點結構獲得的二叉樹存儲結構稱爲三叉鏈表。
樹的遍歷(traversal)是按照某種次序訪問樹中的全部結點,且每一個結點剛好訪問一次。
二叉樹的遍歷是以遞歸的方式進行,依遞歸的調用順序的不一樣,可分爲3種不一樣的遍歷方式:前序遍歷方式、中序遍歷方式、後序遍歷方式。
前序遍歷(Preorder Traversal)是先遍歷根結點,再遍歷左子樹,最後才遍歷右子樹。
操做步驟以下:
中序遍歷(Inorder Traversal)是先遍歷左子樹,再遍歷根結點,最後才遍歷右子樹。
操做步驟以下:
後續遍歷(Postorder Traversal)是先遍歷左子樹,再遍歷右子樹,最後才遍歷根結點。
操做步驟以下:
層次遍歷是指從二叉樹的第一層開始,從上至下逐層遍歷,在同一層中,則按從左到右的順序對結點逐個訪問。
操做步驟以下:
利用空指針域存放結點的前趨結點和後繼幾點的指針信息,這種附加的指針稱爲線索。
增長了線索的二叉鏈表稱爲線索鏈表,相應的二叉樹稱爲線索二叉樹(Threaded Binary Tree)
爲了區分一個結點的指針是指向去兒子仍是指向其前趨後繼,爲每一個結點增長了兩個線索標誌域ltag和rtag,這樣線索鏈表的結點結構以下圖:
標誌域標誌的是左右指針域的功能,以下:
每一個標誌位只佔1bit。
一棵二叉樹以某種方式遍歷並使其變成線索二叉樹的過程稱爲二叉樹的線索化。 以下圖:三種遍歷下的線索二叉樹
爲了討論算法方便起見,一般在二叉樹中增長一個與樹中結點相同類型的頭結點,令頭結點的信息域爲空,其lchild域指向二叉樹的根結點,當二叉樹爲空時,lchild域值爲空;其rchild域指向以某種方式遍歷二叉樹時最後訪問的結點,當二叉樹爲空時,rchild域指向該結點自己,同時令原來指向二叉樹根結點的頭指針指向該頭結點,以某種方式遍歷二叉樹時,第一個被訪問結點的左指針域和最後一個被訪問結點的右指針域的值若是是線索,也指向該頭結點。
如圖:
建立線索二叉樹實質上就是遍歷一棵二叉樹,在遍歷的過程當中,檢查當前結點的指針域是否爲空,若是爲空改成指向前趨結點或者後繼結點的線索。
在堆一個二叉樹加線索時,必須申請一個頭結點,創建頭結點與二叉樹的根結點的線索,對二叉樹線索化後,還需創建最後一個結點與頭結點之間的線索。
總結起來就兩個步驟:
若是該結點的左標誌位爲1,那麼其左指針域所指向的結點即是它的前趨結點。
若是該結點的左標誌位爲0,代表該結點有左兒子,根據中序遍歷的定義,它的前趨結點是以該結點的左兒子爲根的子樹的最右結點。
若是該結點的右標誌位爲1,那麼其右指針域所指向的結點就是它的後繼結點。
若是該結點的右標誌位爲0,代表該結點有右兒子,根據中序遍歷的定義,它的後繼結點就是以該結點右兒子爲根的子樹的最左結點。
順序遍歷比對數據便可。
插入結點可分爲兩種狀況考慮:
每種狀況又分別分爲兩種:
將一顆樹轉換爲二叉樹的步驟以下:
轉換過程以下圖:
在轉換事後的二叉樹中,左分支上的各結點在原來樹中是父子關係,右分支上的各結點在原來的樹中是兄弟關係。
樹轉換爲二叉樹這一轉換過程是可逆的,能夠依據二叉樹的根結點有無右兒子結點,將一顆二叉樹還原爲樹。
步驟以下:
以下圖即爲還原過程:
森林是若干棵樹的集合,森林亦可用二叉樹表示。
轉換步驟以下:
過程如圖:
樹的遍歷分爲兩種方式:先根遍歷和後根遍歷。
先根遍歷定義爲:
後跟遍歷的定義爲:
根據樹與二叉樹的轉換關係以及樹與二叉樹的遍歷定義能夠推出:
所以,樹的遍歷算法也可採用相應的二叉樹的遍歷算法實現。
森林的遍歷有三種方式:前序遍歷、中序遍歷和後續遍歷。
前序遍歷的定義爲: 若森林非空,則:
中序遍歷的定義爲: 若森林非空,則:
後續遍歷的定義爲: 若森林非空,則:
根據森林與二叉樹的轉換關係以及森林和二叉樹的遍歷定義能夠推出:
以一組連續的存儲單元來存放樹中的結點,每一個結點有兩個域:一個是數據域,用來存放結點的信息,另外一個是雙親域,用來存放雙親的位置。 結構如圖所示:
將一個結點全部孩子連接成一個單鏈表形,而樹中有若干個結點,則由若干個單鏈表,每一個單鏈表有一個表頭結點,全部表頭結點用一個數組來描述。 結構如圖:
如圖所示:
相似二叉鏈表,但第一根鏈指向第一個孩子,第二根鏈指向下一個兄弟。 結構如圖:
哈夫曼(Huffman)樹又稱最優樹,能夠用來構造最優編碼,用於信息傳輸、數據壓縮等方面,是一類有着普遍應用的二叉樹。
在數據通訊中,常常須要將傳送的文字轉換成由二進制字符0、1組成的二進制串,咱們稱之爲二進制編碼。 在發送端,須要將電文中的字符轉換成二進制的0、1序列,而在接受端則要將受到的0、1序列轉換成對應的字符序列。
哈弗曼樹可用於構造使電文的編碼總長最短的編碼方案:
規定哈弗曼樹中的左分支表明0,右分支表明1,則從根結點到每一個葉節點所通過的路徑分支組成的0或1序列便爲該結點對應字符的編碼,稱爲哈夫曼編碼。
在哈夫曼編碼樹中,樹的帶權路徑長度的含義是各個字符的碼長與其出現次數的乘積和,也就是電文的代碼總長。
由於哈夫曼算法構造的是帶權路徑長度最小的二叉樹,因此採用哈夫曼樹構造的編碼是一種能使電文代碼總長最短的不等長編碼。
實現哈夫曼樹的算法可分爲兩大部分,構造哈夫曼樹和在哈夫曼樹上求葉結點的編碼。
在構造哈夫曼樹時,能夠設置一個結構數組huff_node保存哈夫曼樹中各結點的信息,根據二叉樹的性質可知,huff_node數組的大小可設置爲2n-1。 數組元素的結構以下:
上一篇:數組和廣義表
下一篇:圖(graph)