本系列文章經補充和完善,已修訂整理成書《Java編程的邏輯》,由機械工業出版社華章分社出版,於2018年1月上市熱銷,讀者好評如潮!各大網店和書店有售,歡迎購買,京東自營連接:http://item.jd.com/12299018.htmlhtml
40節介紹了HashMap,41節介紹了HashSet,它們的共同實現機制是哈希表,一個共同的限制是沒有順序,咱們提到,它們都有一個能保持順序的對應類TreeMap和TreeSet,這兩個類的共同實現基礎是排序二叉樹,爲了更好的理解TreeMap/TreeSet,本節咱們先來介紹排序二叉樹的一些基本概念和算法。算法
基本概念數據庫
先來講樹的概念,現實中,樹是從下往上長的,樹會分叉,在計算機程序中,通常而言,與現實相反,樹是從上往下長的,也會分叉,有個根節點,每一個節點能夠有一個或多個孩子節點,沒有孩子節點的節點通常稱爲葉子節點。編程
二叉樹是一個樹,但,每一個節點最多有兩個孩子節點,一左一右,左邊的稱爲左孩子,右邊的稱爲右孩子,咱們看兩個例子,以下所示:數組
這兩棵樹都是二叉樹,左邊的根節點爲5,除了葉子節點外,每一個節點都有兩個孩子節點,右邊的根節點爲7,有的節點有兩個孩子節點,有的只有一個。微信
樹有一個高度或深度的概念,是從根到葉子節點通過的節點個數的最大值,左邊樹的高度爲3,右邊的爲5。數據結構
排序二叉樹也是二叉樹,但,它沒有重複元素,並且是有序的二叉樹,什麼順序呢?對每一個節點而言:性能
上面的兩顆二叉樹都是排序二叉樹。好比說左邊的樹,根節點爲5,左邊的都小於5,右邊的都大於5。再看右邊的樹,根節點爲7,左邊的都小於7,右邊的都大於7,在以3爲根的左子樹中,其右子樹的值都大於3。spa
排序二叉樹有什麼優勢?如何在樹中進行基本操做如查找、遍歷、插入和刪除呢?咱們來看一下基本的算法。htm
基本算法
查找
排序二叉樹有一個很好的優勢,在其中查找一個元素是很方便、也很高效的,基本步驟爲:
這個步驟與在數組中進行二分查找或者說折半查找的思路是相似的,若是二叉樹是比較平衡的,相似上圖中左邊的二叉樹,則每次比較都能將比較範圍縮小一半,效率很高。
此外,在排序二叉樹中,能夠方便的查找最小最大值,最小值即爲最左邊的節點,從根節點一路查找左孩子便可,最大值即爲最右邊的節點,從根節點一路查找右孩子便可。
遍歷
排序二叉樹也能夠方便的按序遍歷,用遞歸的方式,用以下算法便可按序遍歷:
好比,遍歷訪問下面的二叉樹:
從根節點開始,但先訪問根節點的左子樹,一直到最左邊的節點,因此第一個訪問的是1,1沒有右子樹,返回上一層,訪問3,而後訪問3的右子樹,4沒有左子樹,因此訪問4,而後是4的右子樹6,依次類推,訪問順序就是有序的:1 3 4 6 7 8 9。
不用遞歸的方式,也能夠實現按序遍歷,第一個節點爲最左邊的節點,從第一個節點開始,依次找後繼節點。給定一個節點,找其後繼節點的算法爲:
文字描述比較抽象,咱們來看個圖,以上圖爲例,每一個節點的後繼以下圖綠色箭頭所示:
對每一個節點,對照算法,咱們再詳細解釋下:
怎麼構建排序二叉樹呢?能夠在插入、刪除元素的過程當中造成和保持。
插入
在排序二叉樹中,插入元素首先要找插入位置,即新節點的父節點,怎麼找呢?與查找元素相似,從根節點開始往下找,其步驟爲:
找到父節點後,便可插入,若是插入元素小於父節點,則做爲左孩子插入,不然做爲右孩子插入。
咱們來看個例子,依次插入7, 3, 4, 1, 9, 6, 8的過程,這個過程以下圖所示:
刪除
從排序二叉樹中刪除一個節點要複雜一些,有三種狀況:
咱們分別來看下。
若是節點爲葉子節點,則很簡單,能夠直接刪掉,修改父節點的對應孩子爲空便可。
若是節點只有一個孩子節點,則替換待刪節點爲孩子節點,或者說,在孩子節點和父節點之間直接創建連接。好比說,在下圖中,左邊二叉樹中刪除節點4,就是讓4的父節點3與4的孩子節點6直接創建連接。
若是節點有兩個孩子,則首先找該節點的後繼(根據以前介紹的後繼算法,後繼爲右子樹中最小的節點,這個後繼必定沒有左孩子),找到後繼後,替換待刪節點爲後繼的內容,而後再刪除後繼節點。後繼節點沒有左孩子,這就將兩個孩子的狀況轉換爲了葉子節點或只有一個孩子的狀況。
好比說,在下圖中,從左邊二叉樹中刪除節點3,3有兩個孩子,後繼爲4,首先替換3的內容爲4,而後再刪除節點4。
平衡的排序二叉樹
從前面的描述中能夠看出,排序二叉樹的形狀與插入和刪除的順序密切相關,極端狀況下,排序二叉樹可能退化爲一個鏈表,好比說,若是插入順序爲:1 3 4 6 7 8 9,則排序二叉樹形狀爲:
退化爲鏈表後,排序二叉樹的優勢就都沒有了,即便沒有退化爲鏈表,若是排序二叉樹高度不平衡,效率也會變的很低。
平衡具體定義是什麼呢?有一種高度平衡的定義,即任何節點的左右子樹的高度差最多爲一。知足這個平衡定義的排序二叉樹又被稱爲AVL樹,這個名字源於它的發明者G.M. Adelson-Velsky 和 E.M. Landis,在他們的算法中,在插入和刪除節點時,經過一次或屢次旋轉操做來從新平衡樹。
在TreeMap的實現中,用的並非AVL樹,而是紅黑樹,與AVL樹相似,紅黑樹也是一種平衡的排序二叉樹,也是在插入和刪除節點時經過旋轉操做來平衡的,但它並非高度平衡的,而是大體平衡的,所謂大體是指,它確保,對於任意一條從根到葉子節點的路徑,沒有任何一條路徑的長度會比其餘路徑長過兩倍。紅黑樹減弱了對平衡的要求,但下降了保持平衡須要的開銷,在實際應用中,統計性能高於AVL樹。
爲何叫紅黑樹呢?由於它對每一個節點進行着色,顏色或黑或紅,並對節點的着色有一些約束,知足這個約束便可以確保樹是大體平衡的。
對AVL樹和紅黑樹,它們保持平衡的細節都是比較複雜的,咱們就不介紹了,咱們須要知道的就是,它們都是排序二叉樹,都經過在插入和刪除時執行開銷不大的旋轉操做保持了樹的高度平衡或大體平衡,從而保證了樹的查找效率。
小結
本節介紹了排序二叉樹的基本概念和算法。
排序二叉樹保持了元素的順序,並且是一種綜合效率很高的數據結構,基本的保存、刪除、查找的效率都爲O(h),h爲樹的高度,在樹平衡的狀況下,h爲log2(N),N爲節點數,好比,若是N爲1024,則log2(N)爲10。
基本的排序二叉樹不能保證樹的平衡,可能退化爲一個鏈表,有不少保持樹平衡的算法,AVL樹是第一個,能保證樹的高度平衡,但紅黑樹是實際中使用更爲普遍的,雖然只能保證大體平衡,但下降了維持樹平衡須要的開銷,總體統計效果更好。
與哈希表同樣,樹也是計算機程序中一種重要的數據結構和思惟方式。爲了可以快速操做數據,哈希和樹是兩種基本的思惟方式,不須要順序,優先考慮哈希,須要順序,考慮樹。除了容器類TreeMap/TreeSet,數據庫中的索引結構也是基於樹的(不過基於B樹,而不是二叉樹),而索引是可以在大量數據中快速訪問數據的關鍵。
理解了排序二叉樹的基本概念和算法,理解TreeMap和TreeSet就比較容易了,讓咱們在接下來的兩節中探討這兩個類。
---------------
未完待續,查看最新文章,敬請關注微信公衆號「老馬說編程」(掃描下方二維碼),從入門到高級,深刻淺出,老馬和你一塊兒探索Java編程及計算機技術的本質。用心原創,保留全部版權。