從標準二叉樹的極端狀況咱們推導出2-3樹這樣的數據結構具有自平衡的特性,可是要實現這個特性在算法上至關複雜。考慮在大部分狀況下,對於檢索的指數級時間消費O(lgN)要求並不嚴格。所以,咱們會看到如何將一顆標準的2-3樹轉變成紅黑樹的過程。算法
1、局部變換數據結構
考慮若是在2-節點上掛新的鍵並不會破壞2-3樹的平衡結構。但是在3-節點上掛新的鍵,可能的變化卻多達6種。這個臨時的4-節點多是根節點,多是一個2-節點的左子節點或者右子節點,也多是3-節點的左子節點、中子節點或者右子節點。2-3樹插入算法的根本在於這些變換都是局部的:除了相關的節點和連接以外沒必要修改該或者檢查樹的其餘部分。所以,每次變換中,變動的連接數量都不會超過一個很小的常熟。this
每個變換都會將4-節點中的一個鍵送入它的父節點中,並重構相應的連接,但沒必要涉及樹的其餘部分。這也是2-3樹的重要特性:一次聚聚變換不會影響樹的有序和平衡性:spa
2、紅黑樹與2-3樹的相互轉換3d
儘管2-3樹一種高效的結構並提供了自平衡特性,可是它的算法過於複雜以致於在通常的狀況下咱們並不肯意使用這樣的結構做爲首選方案。可是紅黑樹則否則,它的實現算法很是簡單,代碼量也不大。但理解整個紅黑樹的構造過程卻須要一番仔細的探究。code
紅黑二叉查找樹背後的基本思想是用標準的二叉查找樹和一些額外信息來表示2-3樹。咱們將樹中的連接分爲兩種類型:紅鏈接將2個2-節點鏈接起來構成一個3-節點,黑連接則是2-3樹中的普通連接。對象
對於紅黑樹咱們給出的定義以下:blog
1.紅鏈接均爲左鏈接;io
2.沒有任何一個節點同時和兩條紅鏈接相連;class
3.該樹是完美黑色平衡的,即任意空節點到根節點的路徑上的黑色連接數量相等。
咱們只須要將一顆紅黑樹中的紅鏈接畫平,那麼全部的空節點到根節點的距離都是相同的。若是咱們將由紅鏈接項鍊的節點合併,獲得的就是一顆2-3樹。
方便起見,由於每個節點都只會由一條指向本身的連接,咱們將連接的顏色泊岸村在表示節點的Node對象中,布爾變量color爲true表示紅色,false爲黑色。節點定義代碼以下:
class Node { private Key key; private Value val; private Node left; private Node right; private boolean color; private int size; public Node(Key key, Value val, boolean color, int size) { this.key = key; this.val = val; this.color = color; this.size = size; } }
3、旋轉和顏色變化
對紅黑樹來講旋轉是它的基礎變化,在實現代碼前咱們先思考一下爲何須要旋轉。
向2-3樹的2-節點插入新的鍵是很是簡單的,不管這個鍵自己是E仍是S,在插入新的鍵之後都一個樣子。但對於紅黑樹來講就不是這樣了,回想一下以前對紅黑樹的定義:紅鏈接均爲左鏈接。若是要插入的新值小於節點咱們能夠直接插入,若是新值大於節點,咱們就會建立一條紅色的右連接。所以咱們就須要經過旋轉將右連接變成左鏈接。固然更復雜的狀況下,咱們還會經過旋轉把左鏈接變成右連接。也許到這裏有人會問:當插入的新鍵大於節點的時候,爲何不能夠直接插入一條黑色的右連接呢?在思考一下2-3樹的生長特性以及紅黑樹對平衡的定義:
1.2-3樹是區別於二叉樹,它是向上生長的。
2.紅黑樹中任一空節點到根節點的距離相同。
綜合起來就是,咱們在紅黑樹中插入的新鍵都必須從紅連接開始,再經過變換來調整樹的高度以恢復平衡。
接下來咱們再考慮一個問題,若是咱們向紅黑樹插入了一條向左的紅連接(顯然此時並未破壞樹的平衡性,所以咱們沒必要調整樹的結構)而後再向同一個節點插入了一條紅色的右連接怎麼辦?左連接已經被佔據了,咱們沒法再經過旋轉調整平衡。
回想一下2-3樹在遇到這樣的狀況是是如何變化的:咱們先構造一個臨時的4-節點,再將它轉換成3個2-節點並把父節點向上轉移。那麼對於紅黑樹來講彷佛就更簡單了一些。咱們只須要調整兩條子連接爲黑連接並把根連接變成紅鏈接便可。
你或許又會問了:若是父節點已是紅色的怎麼辦呢?事實上你徹底不用擔憂這樣的狀況發生,由於紅黑樹的另外一條特性已經保證了:沒有任何一個節點同時和兩條紅鏈接相連。換言之,若是父節點(E)爲紅色,當咱們在插入(A)的時候已經不符合紅黑樹的定義,咱們須要經過旋轉來恢復平衡。
總的來講,不管以前節點的狀況如何,咱們經過0次,1次或2次旋轉以及顏色的變化獲得指望的結果。