說到HashMap,就必定要說到紅黑樹,紅黑樹做爲一種自平衡二叉查找樹,是一種用途較廣的數據結構,在jdk1.8中使用紅黑樹提高HashMap的性能,今天就來講一說紅黑樹。算法
限於篇幅,本文只對紅黑樹的基礎進行說明,暫不涉及源碼部分,大部分摘抄自維基百科,這裏也貼出對應連接:數據結構
維基百科(中文):https://zh.wikipedia.org/wiki...性能
維基百科:https://en.wikipedia.org/wiki...spa
筆者這裏會根據維基百科的講解作些說明,方便初學者理解。code
固然,在瞭解紅黑樹以前,先回顧下樹這種結構的特色(來自維基百科)):遞歸
在計算機科學中,樹(英語:tree)是一種抽象數據類型(ADT)或是實現這種抽象數據類型的數據結構,用來模擬具備樹狀結構性質的數據集合。它是由n(n>0)個有限節點組成一個具備層次關係的集合。把它叫作「樹」是由於它看起來像一棵倒掛的樹,也就是說它是根朝上,而葉朝下的。它具備如下的特色:每一個節點都只有有限個子節點或無子節點;
沒有父節點的節點稱爲根節點;
每個非根節點有且只有一個父節點;
除了根節點外,每一個子節點能夠分爲多個不相交的子樹;
樹裏面沒有環路(cycle)ip紅黑樹(英語:Red–black tree)是一種
自平衡二叉查找樹
,紅黑樹和AVL樹同樣都對插入時間、刪除時間和查找時間提供了最好可能的最壞狀況擔保。
紅黑樹的結構複雜,但它的操做有着良好的最壞狀況運行時間,而且在實踐中高效:它能夠在O(log n)時間內完成查找,插入和刪除,這裏的O(log n) n是樹中元素的數目。rem
這些描述說明了紅黑樹結構的一大特色:在增刪查上即便是最壞的一種數據結構狀況,也保證了性能。get
有些新手可能會想,爲何能保證?源碼
看完整篇文章,你可能就會了解清楚,紅黑樹概念上其實也說起了,自平衡
這個詞說明了每次在咱們進行增刪時,會自行調整結構來知足紅黑樹特性,這樣在查詢上就能保證性能。
那麼,什麼樣的二叉樹纔是紅黑樹呢?
紅黑樹是每一個節點都帶有顏色屬性的二叉查找樹,顏色爲紅色或黑色。在二叉查找樹強制通常要求之外,對於任何有效的紅黑樹咱們增長了以下的額外要求:1.節點是紅色或黑色。
2.根是黑色。
3.全部葉子都是黑色(葉子是NIL節點)。
4.每一個紅色節點必須有兩個黑色的子節點。(從每一個葉子到根的全部路徑上不能有兩個連續的紅色節點。)
5.從任一節點到其每一個葉子的全部簡單路徑都包含相同數目的黑色節點。
這裏須要注意葉子節點的概念與通常意義上樹的葉子節點不一樣,這裏紅黑樹葉子節點都是黑色且爲NIL的。
維基百科上對於這5種性質也作了說明:
這些約束確保了紅黑樹的關鍵特性:從根到葉子的最長的可能路徑很少於最短的可能路徑的兩倍長。結果是這個樹大體上是平衡的。由於操做好比插入、刪除和查找某個值的最壞狀況時間都要求與樹的高度成比例,這個在高度上的理論上限容許紅黑樹在最壞狀況下都是高效的,而不一樣於普通的二叉查找樹。要知道爲何這些性質確保了這個結果,注意到性質4致使了路徑不能有兩個毗連的紅色節點就足夠了。最短的可能路徑都是黑色節點,最長的可能路徑有交替的紅色和黑色節點。由於根據性質5全部最長的路徑都有相同數目的黑色節點,這就代表了沒有路徑能多於任何其餘路徑的兩倍長。
在不少樹數據結構的表示中,一個節點有可能只有一個子節點,而葉子節點包含數據。用這種範例表示紅黑樹是可能的,可是這會改變一些性質並使算法複雜。爲此,本文中咱們使用"nil葉子"或"空(null)葉子",如上圖所示,它不包含數據而只充當樹在此結束的指示。這些節點在繪圖中常常被省略,致使了這些樹好像同上述原則相矛盾,而實際上不是這樣。與此有關的結論是全部節點都有兩個子節點,儘管其中的一個或兩個多是空葉子。
從根到葉子的最長的可能路徑很少於最短的可能路徑的兩倍長
這個在文中也解釋了緣由,由於性質4須要被知足,這樣限制了樹的高度差,使得查找性能提高。
能夠多想下,普通的二叉樹,最壞狀況以下:
這種狀況下使得樹形結構毫無心義,而紅黑樹(其存在的5種特性保證)則不會出現這種狀況。
具體請參考維基百科的證實:
以前的定義我也說了,爲了保證紅黑樹的特性,須要在插入刪除時對樹進行調整來保證自平衡。那麼如何來調整呢?
這裏就說明下紅黑樹平衡的兩種操做:變色和旋轉
。
變色這個操做很好理解了吧,在某些狀況下,爲了保證自平衡,須要改變顏色,來知足紅黑樹的特性
能夠參考維基百科:https://en.wikipedia.org/wiki...
旋轉操做相對要複雜些,旋轉分爲左旋和右旋。
以下圖所示,逆時針旋轉旋轉M,N兩個節點,使得父節點M變爲N的子節點,N變爲父節點,交換以後對於B節點就須要進行調整,B節點變爲M的右子節點
以下圖所示,順時針旋轉旋轉M,N兩個節點,使得父節點M變爲N的子節點,N變爲父節點,交換以後對於B節點就須要進行調整,B節點變爲M的左子節點
這裏貼出維基上的gif圖,方便各位理解:
不管是變色仍是旋轉,最終調整的結果都是要在插入或者刪除以後知足紅黑樹的5個特性。
首先須要明白的是新增長的節點默認標記爲紅色
,至於緣由維基上說明了:
若是設爲黑色,就會致使根到葉子的路徑上有一條路上,多一個額外的黑節點,這個是很難調整的。可是設爲紅色節點後,可能會致使出現兩個連續紅色節點的衝突,那麼能夠經過顏色調換(color flips)和樹旋轉來調整。
在說我我的理解以前,須要理解局部紅黑子樹
含義,其實至關於把紅黑樹進行切割,一個大的紅黑樹切割成多個局部,每次咱們調整都是以一個局部來完成,局部調整完以後,整個樹若是仍是不平衡,則繼續向上回溯調整,維基上也是這樣作的。下圖中的框算是一個局部紅黑子樹。
若是添加的節點默認標記爲黑色,那麼這條路徑上比其餘路徑多了一個黑色節點,不知足性質5,須要調整,可能會影響到其餘已經平衡的局部紅黑子樹,牽一髮而動全身,代價太高,而默認爲紅色,首先其餘已經平衡的局部紅黑子樹還未受到影響,在未調整以前,未平衡的這個局部紅黑子樹中黑色路徑和平衡以前是相同的,這樣應該是要更方便調整。
將要插入的節點標爲N,N的父節點標爲P,N的祖父節點標爲G,N的叔父節點標爲U
這裏也能看出來都是以一個局部來調整,那麼考慮下插入會出現哪些狀況?在考慮的時候必定要注意,在未插入新節點和插入新節點以後變化的地方和須要保持不變的地方。尤爲須要注意插入前已經平衡了,知足紅黑樹的5種特性,不是隨便什麼狀態都會出現,切記。
插入會出現4種狀況:1.N爲根節點,即紅黑樹的根節點。
2.N的父節點(P)是黑色的。
3.N的父節點(P)是紅色的(所以它不能是樹的根)而N的叔父節點(U)是紅色的。
4.N的父節點(P)是紅色的(所以它不能是樹的根)而N的叔父節點(U)是黑色的。
思考下,上面包含的狀況能夠從插入節點N爲紅色開始進行考慮,判斷父節點顏色,根據狀況進行調整,先進行變色操做,變色保證不了平衡則須要旋轉來平衡,而3和4的狀況,有人可能會想爲何要判斷U側的子樹部分?咱們能夠想一下,在P爲紅色,N爲紅色時,在N這一側的子樹部分,如何調整也不會達到平衡,此時只能向上回溯尋求幫助,涉及到了祖父節點G,那麼此時就會影響到G的子樹U部分,因此須要考慮U的顏色而後繼續調整。
插入時按順序判斷上述4種狀況,注意先對局部紅黑子樹(從N到G,G至關於局部根節點)進行平衡,知足屬性4和5,最後判斷G節點是否爲黑色,非黑色以G爲新增節點再次遞歸順序判斷(至關於向上回溯),這裏有一個要注意的地方,局部根節點G能夠爲紅色,除非G是整個樹的根節點,這時直接修改顏色便可完成平衡(至關於整個樹黑色路徑都加一):
CASE-1:N爲根節點,修改爲黑色,一樣知足紅黑樹的5個屬性,僅僅黑色路徑增長了1。不知足當前狀況,繼續CASE-2狀況判斷處理。
CASE-2:P爲黑色,添加節點N爲紅色,N下添加兩個黑色葉子節點(NIL),屬性4和5沒有破壞,不須要調整。不知足當前狀況,繼續CASE-3狀況判斷處理。
CASE-3:P(父)和U(叔)爲紅色,以N爲P的左子節點爲例(右子節點操做相似),添加節點N爲紅色,先進行變色操做,P,U變爲黑色,這樣不知足屬性5,路徑上黑色節點多了1,將G(祖父)變爲紅色,到這裏能夠看出添加節點以後從G到G下的葉子節點路徑和未添加N節點以前的路徑黑色節點數是一致的,在這部分局部區域已經知足屬性4和5,不須要進行調整了,可是G可能違反了屬性2(根節點爲黑色),或屬性4(不能有連續的兩個紅色節點),回到CASE-1再次順序判斷處理,這裏注意下,從新判斷是以G節點爲新局部紅黑子樹的新增節點N,G節點的子節點能夠當作葉子節點,再從新重頭判斷這個新的局部子樹(至關於向上回溯,直到知足條件,或者根節點,知足CASE-1)。不知足當前狀況,繼續CASE-4狀況判斷處理。
CASE-4:P(父)爲紅色,U(叔)爲黑色,以P爲其父節點左子節點爲例,對稱狀況操做相似,變色也知足不了G到葉子節點屬性4和屬性5,這裏就須要考慮另外一種處理-旋轉。
這裏會出現兩種狀態(對稱的狀況自行參照處理):
CASE-4-1:新添加節點N爲P(父)節點右子樹,至關於G樹的「內部」,參考下圖,在這種狀況下,執行的P上的左旋轉。轉換成右側圖,到此仍是違反屬性4,可是不違反屬性5,再繼續CASE-4-2狀況判斷處理。不知足當前狀況,也繼續CASE-4-2狀況判斷處理。
CASE-4-2:新添加節點N爲P(父)節點左子樹,至關於G樹的「外部」,和第一種狀態調整完一致,參考下圖,在這種狀況下,執行G上的右旋轉,P和G的顏色切換,便可知足紅黑樹的屬性,變爲右側圖。
從上面分析過程能夠看出,局部插入最多兩次旋轉,更多的是變色操做,少許的旋轉操做能夠鎖住某些節點(變色操做不影響),大部分節點是能夠查詢且修改的,這也是大部分底層實現使用紅黑樹的緣由吧。
刪除操做比較複雜,下一章再進行說明,本章主要從基礎說明紅黑樹的特性以及相關的平衡操做以及插入新節點時不一樣狀況的調整規則,但願對讀者有所幫助,下面經過流程圖來幫助各位更好的理解和實現紅黑樹插入狀況下的自平衡處理。
下一章我就更爲複雜的刪除操做進行講解,謝謝各位閱讀,若有錯誤,歡迎留言指正,我將盡快驗證修改。
參考資料: