前言:回顧一下前面學習的內容,大概說了下數據結構中的線性結構,從物理存儲方面來說又分爲順序存儲和鏈式存儲結構,各自有自己的優缺點,順序存儲結構讀快寫慢,鏈式存儲結構寫快讀慢。但是這些數據元素之間的關係都爲一對一的關係,而我們生活中關係不止是一對一,有可能是一對多,多對多,本篇先介紹一下一對多的存儲結構,那麼它是怎樣存儲才能保持它們之間的關係呢?
一、樹定義
生活中有很多這樣的例子,一個強盛家族的族譜,必然是呈金字塔形狀的,從一到多。就和樹一樣,一顆樹通常由根部長出一個樹幹,然後從樹幹長出一些樹枝,然後從樹枝上又長出更小的樹枝,而葉子則長在最細的樹枝上。樹這種數據結構正式像一顆倒過來的樹木。
所以對樹的定義,即樹是一種管理有像樹幹、樹枝、樹葉一樣關係的數據的數據結構。
樹由節點(頂點)和邊(枝)構成,並且有一個節點作爲起始點。這個起始點稱爲樹的根節點。從根節點上可以連出幾條邊,每條邊都和一個節點相連。延伸出來的這些節點又可以繼續通過邊延伸出新的結點。這個過程中,舊的節點稱作父結點,而延伸出來的新節點稱作子結點。一個子節點都沒有的節點就叫做葉子結點。另外,由根節點出發,到達某一個節點所要經過的邊的個數叫做這個節點的深度,節點擁有的子樹數稱爲該節點的度,樹的度爲各節點度的最大值。
下圖中,標着7、9和0三個數字的節點裏,7就是父節點,9和0就是子節點。另一個圖裏,標記6的這個節點就是“根節點”,標記3、5、11、8、10、9、2、17、14的這些節點就是“葉子節點”,而圖中12這個的深度是2。
樹(Tree)是n(n>=0)個節點的有限集。當n=0時稱爲空樹,在任意一顆非空樹中:
- 有且僅有一個特定的稱爲根(Root)的節點;
- 當n>1時,其餘節點可分爲m(m>0)個互不相交的有限集T1、T2、、、Tm,其中每一個集合本身由是一棵樹,並且稱爲根的子樹(SubTree)。
節點的子樹的根稱爲節點的孩子(Child),相應的該節點稱爲孩子的雙親(Parent),同一雙親的孩子之間互稱爲兄弟(Sibling);節點的祖先是從根到該節點所經分支上的所有結點。
總結一下樹的相關概念:
節點的度:一個節點含有的子樹的個數稱爲該節點的度; 葉節點或終端節點:度爲0的節點稱爲葉節點; 非終端節點或分支節點:度不爲0的節點; 雙親節點或父節點:若一個節點含有子節點,則這個節點稱爲其子節點的父節點; 孩子節點或子節點:一個節點含有的子樹的根節點稱爲該節點的子節點; 兄弟節點:具有相同父節點的節點互稱爲兄弟節點; 樹的度:一棵樹中,最大的節點的度稱爲樹的度; 節點的層次:從根開始定義起,根爲第1層,根的子節點爲第2層,以此類推; 樹的高度或深度:樹中節點的最大層次; 堂兄弟節點:雙親在同一層的節點互爲堂兄弟; 節點的祖先:從根到該節點所經分支上的所有節點; 子孫:以某節點爲根的子樹中任一節點都稱爲該節點的子孫。 森林:由m(m>=0)棵互不相交的樹的集合稱爲森林;
二、樹的存儲結構
上面已經對樹的定義和概念做了個基本的認識,那麼我們最關心的還是怎麼在程序中實現該結構。
在計算機中數據的存儲有兩種結構順序存儲和鏈式存儲,順序存儲結構顯然是不行的,而鏈式存儲結構也是有缺點的,我們來看一下:
第一種:
由於鏈式存儲結構中的節點需含有子結點的引用或指針,但在樹中子節點的不確定性導致無法無法固定具體節點中有幾個引用或指針;
Node節點結構示意圖:
我們可以根據樹的度來確定Node節點的結構,比如樹的度爲3,那麼Node結構中就由3個對自己引用。
這樣的話就會浪費很多空間,所以這樣的結構不是最理想的存儲結構;
第一種(改):
對於上面的存儲結構會過多的浪費存儲空間,那麼我們在結點中聲明一個動態鏈表Nodes來存放可能的子節點Node;
修改完後的結構如圖:
第二種:
使用數組+鏈表結合的方式來表示樹:
在第一版(改)中可以看到Node使用了集合,那麼爲什麼不直接使用數組+鏈表的方式來構建呢,所以構建出來後就是上面圖的樣子(0的位置爲ROOT根節點,從根節點出發,查看每個分支,構成樹)。
根據上面的存儲方式,就可以完成對樹的實現了;
三、樹的分類
樹的種類分很多,根據是子節點之間否有序可分爲有序樹和無序樹;
根據子節點數量分爲二叉樹和多路查找樹;
而其中二叉樹又分爲斜二叉樹、滿二叉樹、完全二叉樹、線索二叉樹、二叉排序樹、平衡二叉樹、紅黑樹、哈夫曼樹等。多路查找樹分爲2-3樹、2-3-4樹、B樹、B樹變種樹B+樹等。
有句話是這樣說的,一木成樹,兩木成林,三木成森,所以有樹就有森林,即n棵互不相交的樹稱爲森。
本系列參考書籍:
《寫給大家看的算法書》
《圖靈程序設計叢書 算法 第4版》