http://blog.csdn.net/yang_yulei/article/details/26066409算法
http://blog.csdn.net/yang_yulei/article/details/26104921 數組
二叉查找樹和快速排序幾乎就是「雙胞胎」。樹的根結點就是快速排序中的第一個切分元素(左側的鍵都比它小,右側的鍵都比它大),而這對於全部的子樹一樣適用,這和快速排序中對於子數組的遞歸排序徹底對應。【在由N個隨機鍵構造的二叉查找樹中,查找命中平均所需的比較次數爲~2lgN。 N越大這個公式越準確】不幸的是,在動態插入中保證樹的完美平衡的代價過高了。咱們放鬆對完美平衡的要求,使符號表API中全部操做均可以在對數時間內完成。性能
要在2-3樹中插入一個新結點,咱們能夠和二叉查找樹同樣先進行一次未命中的查找,而後把新結點掛在樹的底部。但這樣的話樹沒法保持完美平衡性。咱們使用2-3樹的主要緣由就在於它可以在插入以後繼續保持平衡。若是未命中的查找結束於一個2-結點,咱們只要把這個2-結點替換爲一個3-結點,將要插入的鍵保存在其中便可。若是未命中的查找結束於一個3-結點,事情就要麻煩一些。spa
先考慮最簡單的例子:只有一個3-結點的樹,向其插入一個新鍵。.net
這棵樹惟一的結點中已經沒有可插入的空間了。咱們又不能把新鍵插在其空結點上(破壞了完美平衡)。爲了將新鍵插入,咱們先臨時將新鍵存入該結點中,使之成爲一個4-結點。建立一個4-結點很方便,由於很容易將它轉換爲一顆由3個2-結點組成的2-3樹(如圖所示),這棵樹既是一顆含有3個結點的二叉查找樹,同時也是一顆完美平衡的2-3樹,其中全部空連接到根結點的距離都相等。blog
向一個父結點爲2-結點的3-結點中插入新鍵排序
假設未命中的查找結束於一個3-結點,而它的父結點是一個2-結點。在這種狀況下咱們須要在維持樹的完美平衡的前提下爲新鍵騰出空間。遞歸
咱們先像剛纔同樣構造一個臨時的4-結點並將其分解,但此時咱們不會爲中鍵建立一個新結點,而是將其移動至原來的父結點中。(如圖所示)get
此次轉換也並不影響(完美平衡的)2-3樹的主要性質。樹仍然是有序的,由於中鍵被移動到父結點中去了,樹仍然是完美平衡的,插入後全部的空連接到根結點的距離仍然相同。效率
向一個父結點爲3-結點的3-結點中插入新鍵
假設未命中的查找結束於一個3-結點,而它的父結點是一個3-結點。
咱們再次和剛纔同樣構造一個臨時的4-結點並分解它,而後將它的中鍵插入它的父結點中。但父結點也是一個3-結點,所以咱們再用這個中鍵構造一個新的臨時4-結點,而後在這個結點上進行相同的變換,即分解這個父結點並將它的中鍵插入到它的父結點中去。
咱們就這樣一直向上不斷分解臨時的4-結點並將中鍵插入更高的父結點,直至遇到一個2-結點並將它替換爲一個不須要繼續分解的3-結點,或者是到達3-結點的根。
先找插入結點,若結點有空(即2-結點),則直接插入。如結點沒空(即3-結點),則插入使其臨時容納這個元素,而後分裂此結點,把中間元素移到其父結點中。對父結點亦如此處理。(中鍵一直往上移,直到找到空位,在此過程當中沒有空位就先搞個臨時的,再分裂。)理解了2-3樹的插入過程也就基本理解了紅黑樹的插入。
構造
和標準的二叉查找樹由上向下生長不一樣,2-3樹的生長是由下向上的。
優勢:2-3樹在最壞狀況下仍有較好的性能。每一個操做中處理每一個結點的時間都不會超過一個很小的常數,且這兩個操做都只會訪問一條路徑上的結點,因此任何查找或者插入的成本都確定不會超過對數級別。
完美平衡的2-3樹要平展的多。例如,含有10億個結點的一顆2-3樹的高度僅在19到30之間。咱們最多隻須要訪問30個結點就能在10億個鍵中進行任意查找和插入操做。
缺點:咱們須要維護兩種不一樣類型的結點,查找和插入操做的實現須要大量的代碼,並且它們所產生的額外開銷可能會使算法比標準的二叉查找樹更慢。
平衡一棵樹的初衷是爲了消除最壞狀況,但咱們但願這種保障所需的代碼可以越少越好。
紅黑樹就是用紅連接表示3-結點的2-3樹。
那麼紅黑樹的插入、構造就可轉化爲2-3樹的問題,即:在腦中用2-3樹來操做,獲得結果,再把結果中的3-結點轉化爲紅連接便可。而2-3樹的插入,前面已有詳細圖文,實際也很簡單:有空則插,沒空硬插,再分裂。 這樣,咱們就不用記那麼複雜且讓人頭疼的紅黑樹插入旋轉的各類狀況了。只要清楚2-3樹的插入方式便可。 下面圖文詳細演示。)
紅黑樹的本質:
紅黑樹是對2-3查找樹的改進,它能用一種統一的方式完成全部變換。
替換3-結點
★紅黑樹背後的思想是用標準的二叉查找樹(徹底由2-結點構成)和一些額外的信息(替換3-結點)來表示2-3樹。
咱們將樹中的連接分爲兩種類型:紅連接將兩個2-結點鏈接起來構成一個3-結點,黑連接則是2-3樹中的普通連接。確切地說,咱們將3-結點表示爲由一條左斜的紅色連接相連的兩個2-結點。
這種表示法的一個優勢是,咱們無需修改就能夠直接使用標準二叉查找樹的get()方法。對於任意的2-3樹,只要對結點進行轉換,咱們均可以當即派生出一顆對應的二叉查找樹。咱們將用這種方式表示2-3樹的二叉查找樹稱爲紅黑樹。
紅黑樹的另外一種定義是知足下列條件的二叉查找樹:
⑴紅連接均爲左連接。
⑵沒有任何一個結點同時和兩條紅連接相連。
⑶該樹是完美黑色平衡的,即任意空連接到根結點的路徑上的黑連接數量相同。
若是咱們將一顆紅黑樹中的紅連接畫平,那麼全部的空連接到根結點的距離都將是相同的。若是咱們將由紅連接相連的結點合併,獲得的就是一顆2-3樹。
相反,若是將一顆2-3樹中的3-結點畫做由紅色左連接相連的兩個2-結點,那麼不會存在可以和兩條紅連接相連的結點,且樹必然是完美平衡的。
不管咱們用何種方式去定義它們,紅黑樹都既是二叉查找樹,也是2-3樹。
(2-3樹的深度很小,平衡性好,效率高,可是其有兩種不一樣的結點,實際代碼實現比較複雜。而紅黑樹用紅連接表示2-3樹中另類的3-結點,統一了樹中的結點類型,使代碼實現簡單化,又不破壞其高效性。)
顏色表示:
由於每一個結點都只會有一條指向本身的連接(從它的父結點指向它),咱們將連接的顏色保存在表示結點的Node數據類型的布爾變量color中(若指向它的連接是紅色的,那麼該變量爲true,黑色則爲false)。
當咱們提到一個結點顏色時,咱們指的是指向該結點的連接的顏色。
旋轉
在咱們實現的某些操做中可能會出現紅色右連接或者兩條連續的紅連接,但在操做完成前這些狀況都會被當心地旋轉並修復。
(咱們在這裏不討論旋轉的幾種狀況,把紅黑樹看作2-3樹,天然能夠獲得正確的旋轉後結果)
插入
在插入時咱們可使用旋轉操做幫助咱們保證2-3樹和紅黑樹之間的一一對應關係,由於旋轉操做能夠保持紅黑樹的兩個重要性質:有序性和完美平衡性。
熱身:
向2-結點中插入新鍵
(向紅黑樹中插入操做時,想一想2-3樹的插入操做。紅黑樹與2-3樹在本質上是相同的,只是它們對3結點的表示不一樣。
向一個只含有一個2-結點的2-3樹中插入新鍵後,2-結點變爲3-結點。咱們再把這個3-結點轉化爲紅結點便可)
向一顆雙鍵樹(即一個3-結點)中插入新鍵
(向紅黑樹中插入操做時,想一想2-3樹的插入操做。你把紅黑樹當作2-3樹來處理插入,一切都變得簡單了)
(向2-3樹中的一個3-結點插入新鍵,這個3結點臨時成爲4-結點,而後分裂成3個2結點)
★一顆紅黑樹的構造全過程
平衡二叉樹(AVL樹)
定義:平衡二叉樹(Balance Binary Tree)又稱AVL樹。它或者是一顆空樹,或者是具備下列性質的二叉樹:它的左子樹和右子樹都是平衡二叉樹,且左子樹和右子樹的深度之差的絕對值不超過1。
若將二叉樹上結點的平衡因子BF(BalanceFactor)定義爲該結點的左子樹深度減去它的右子樹深度,則平衡因子的絕對值大於1。
其旋轉操做 用2-3樹的分裂來類比想象。