爲何要有紅黑樹?什麼是紅黑樹?畫了20張圖,看完這篇你就明白了

爲何要有紅黑樹

想必你們對二叉樹搜索樹都不陌生,首先看一下二叉搜索樹的定義:
二叉搜索樹(Binary Search Tree),或者是一棵空樹,或者是具備下列性質的二叉樹: 若它的左子樹不空,則左子樹上全部結點的值均小於它的根結點的值; 若它的右子樹不空,則右子樹上全部結點的值均大於它的根結點的值; 它的左、右子樹也分別爲二叉排序樹。
從理論上來講,二叉搜索樹的查詢、插入和刪除一個節點的時間複雜度均爲O(log(n)),已經徹底能夠知足咱們的要求了,那麼爲何還要有紅黑樹呢?
咱們來看一個例子,向二叉搜索樹中依次插入(1,2,3,4,5,6),插入以後是這樣的
退化成鏈表的二叉搜索樹
能夠看到,在這種狀況下,二叉搜索樹退化成了鏈表!!!這時候查詢、插入和刪除一個元素的時候,時間複雜度變成了O(n),顯然這是不能接受的。出現這種狀況狀況的緣由是二叉搜索樹沒有自平衡的機制,因此就有了平衡二叉樹的概念。
平衡二叉樹(Balanced Binary Tree)具備如下性質:它是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,而且左右兩個子樹都是一棵平衡二叉樹。
仍是剛剛的例子,假如咱們用平衡二叉樹來實現的話,插入完元素後應該是下面這樣的(不惟一)
自平衡的二叉樹
平衡二叉樹保證了在最差的狀況下,二叉樹依然可以保持絕對的平衡,即左右兩個子樹的高度差的絕對值不超過1。可是這又會帶來一個問題,那就是平衡二叉樹的定義過於嚴格,致使每次插入或者刪除一個元素以後,都要去維護二叉樹總體的平衡,這樣產生額外的代價又太大了。二叉搜索樹可能退化成鏈表,而平衡二叉樹維護平衡的代價開銷又太大了,那怎麼辦呢?這就要談到「中庸之道」的智慧了。說白了就是把平衡的定義適當放寬,不那麼嚴格,這樣二叉樹既不會退化成鏈表,維護平衡的開銷也能夠接受。沒錯,這就是咱們要談的紅黑樹了。首先看一下紅黑樹的定義:
紅黑樹是一種含有紅黑結點並能自平衡的二叉查找樹。它必須除了知足二叉搜索樹的性質外,還要知足下面的性質:
性質1:每一個節點要麼是黑色,要麼是紅色。
性質2:根節點是黑色。
性質3:每一個葉子節點(NIL)是黑色。
性質4:每一個紅色結點的兩個子結點必定都是黑色。
性質5:任意一結點到每一個葉子結點的路徑都包含數量相同的黑結點。
這就是紅黑樹的五條性質。我相信不少人都看到過,能背下來的也不在少數,可是真正理解爲何要這樣定義的恐怕就很少了。下面就從2-3樹的角度來談談紅黑樹的定義。算法

從2-3樹來看紅黑樹

通常咱們接觸最多的是二叉樹,也就是一個父節點最多有兩個子節點。2-3樹與二叉樹的不一樣之處在於,一個父節點能夠有兩個子節點,也能夠有三個子節點,而且其也知足相似二叉搜索樹的性質。還有最重要的,2-3樹的全部葉子節點都在同一層,且最後一層不能有空節點,相似於滿二叉樹。
咱們依次插入10,9,8,7,6,5,4,3,2,1來看一下2-3樹是如何進行自平衡的。
2-3樹在插入元素以前首先要進行一次未命中的查找,而後將元素插入葉子節點中,以後再進行平衡操做,下面具體說明。
首先插入10,以下圖
2-3樹中插入10
而後插入9,9小於10,2-3樹在插入時要將9融入10這個葉子節點中(固然也是根節點),融合完成後以下:
2-3樹中插入9
這是一個3節點,不用執行平衡操做。2-3樹中把有兩個元素,三個子節點的節點稱爲3節點,把有一個元素,兩個子節點的的節點稱爲2節點。
接着插入8,插入8的時候一樣要先融入葉子節點中,以下圖左側所示
2-3樹中插入8
8融入葉子節點後,該結點便擁有了3個元素,不知足2-3樹的定義,這時就要把3節點進行分裂,即把左側和右側的元素分裂爲2節點,而中間的元素抽出,繼續融入其父節點,在這裏便成爲了一個根節點。
繼續插入7,以下
2-3樹中插入7
插入後,各個節點都知足2-3樹的定義,不須要進行平衡操做。
接着插入6,仍是首先找到葉子節點,而後將其融入,以下圖左側所示
2-3樹中插入6
插入後六、七、8三個元素所在的葉子節點再也不知足2-3樹的定義,須要進行分裂,即抽出元素7融入父節點,6和8分裂爲7的左右子節點。
接着插入5,以下圖所示,一樣不須要進行平衡操做
2-3樹中插入5
接着插入4,仍是首先找到葉子節點,而後將其融入,以下圖左側所示
2-3樹中插入4
插入後四、五、6三個元素所在的葉子節點再也不知足2-3樹的定義,須要進行分裂,即抽出元素5融入父節點,4和6分裂爲5的左右子節點。5融入父節點後,該結點便有了五、七、9三個元素,於是須要繼續分裂,元素7成爲新的根節點,5和9成爲7的左右子節點。
接着插入3,3融入4所在的葉子節點中,不須要進行平衡操做
2-3樹中插入3
接着插入2,仍是首先找到葉子節點,而後將其融入,以下圖左側所示
2-3樹中插入2
插入後二、三、4三個元素所在的葉子節點再也不知足2-3樹的定義,須要進行分裂,即抽出元素3融入父節點,2和4分裂爲3的左右子節點,3融入5所在的父節點中。
最後插入2,一樣先找到葉子節點,而後將其融入,以下圖所示
2-3樹中插入1
至此,咱們便完成了在2-3樹中依次插入10,9,8,7,6,5,4,3,2,1,而且2-3樹始終維護着平衡。怎麼樣,是否是很神奇。編程

再看紅黑樹

那麼紅黑樹與2-3樹有什麼關係呢?如今咱們對2-3樹進行改造,改形成一個二叉樹。怎麼改造呢?對於2節點,保持不變;對於3節點,咱們首先將3節點中左側的元素標記爲紅色,以下圖2所示。
2-3樹到紅黑樹的改造
而後咱們將其改形成圖3的形式;再將3節點的位於中間的子節點的父節點設置爲父節點中那個紅色的節點,如圖4的所示;最後咱們將圖4的形式改成二叉樹的樣子,如圖5所示。圖5是否是很熟悉,沒錯,這就是咱們經常提到的大名鼎鼎的紅黑樹了。
下面咱們回過頭再看下紅黑樹的五條性質。
從2-3樹看紅黑樹
性質1:每一個節點要麼是黑色,要麼是紅色。
2-3樹中存在2節點和3節點,3節點中左側的元素即是紅色節點,而其餘的節點即是黑色節點。
性質2:根節點是黑色。
在2-3樹中,根節點只能是2節點或者3節點,2節點與3節點在紅黑樹中的等價形式,以下圖所示
2節點與3節點在紅黑樹中的等價形式
顯然,不管是哪一種狀況,根節點都是黑色的。
性質3:每一個葉子節點(NIL)是黑色。
這裏的葉子節點不是指左右子樹爲空的那個葉子節點,而是指節點不存在子節點或者爲空節點被稱做葉子節點。在性質2中咱們討論的根節點是黑色的都是討論根節點不爲空的狀況,若紅黑樹是一個空樹,那麼根節點天然也是空的葉子節點,這時候葉子節點便必然是黑色的。
性質4:每一個紅色結點的兩個子結點必定都是黑色。
仍是從2-3樹的角度來理解,紅色節點對應2-3樹中3節點左側的元素,那麼它的子節點要麼是2節點,要麼是3節點。不管是2節點仍是3節點對應的節點顏色都是黑色的,這在性質2時已經討論了。
性質5:任意一結點到每一個葉子結點的路徑都包含數量相同的黑結點。
性質5應該是紅黑樹最重要的一條性質了。2-3樹是一顆絕對平衡的樹,即2-3樹中任意一個節點出發,到達葉子節點後所通過的節點數都是同樣的。那麼對應到紅黑樹呢?2-3樹中2節點對應到紅黑樹即是一個黑色的節點,而3節點對應到紅黑樹是一個紅色節點和一個黑色節點。因此,不管是2節點仍是3節點,在紅黑樹中都會對應一個黑色節點。那麼2-3樹中的絕對平衡,在紅黑樹中天然就是任意一結點到每一個葉子結點的路徑都包含數量相同的黑結點了。
相信你們如今已經對紅黑樹的五條性質有了更加深入的體會了。那麼咱們再看下紅黑樹維持平衡的三種操做,即變色、左旋、右旋怎麼理解呢?
首先看一下變色,如下圖爲例,
紅黑樹的變色
在2-3樹中插入節點3後,便再也不知足2-3樹的定義,須要進行分解,將元素2抽出做爲1和3的父節點,而後2繼續向上融合。
對應到紅黑樹中就是,首先插入節點3,在紅黑樹中新插入的節點默認爲紅色,而後不知足定義,因此須要進行分解,分解後各個節點都爲2節點,因此變爲黑色。而2節點須要繼續向上融合,故要變成紅色。
接着看一下右旋轉,如下圖爲例,
紅黑樹的右旋轉
插入元素1後,進行右旋轉操做,首先把2節點與3節點斷開鏈接,同時把2與2的右子樹斷開鏈接,而後把2的右子樹鏈接至3的左子樹位置,不會違背二分搜索樹的性質,而後再把3鏈接至2的右子樹位置。最後還要改變對應節點的顏色,即把2節點的顏色改成原來3節點的黑色,把3節點的顏色改成原來2節點的紅色。
接着看一下左旋轉,與右旋轉相似,如下圖爲例,
紅黑樹的左旋轉
插入元素3後,進行左旋轉操做,首先把2節點與3節點斷開鏈接,同時把3與3的左子樹斷開鏈接,而後把3的左子樹鏈接至2的右子樹位置,不會違背二分搜索樹的性質,而後再把2鏈接至3的左子樹位置。最後還要改變對應節點的顏色,即把2節點的顏色改成原來3節點的紅色,把3節點的顏色改成原來2節點的黑色。blog

寫在最後

最後須要說的是,本文中提到的紅黑樹是一種特殊的紅黑樹——左傾紅黑樹,即紅色節點都是父節點的左子樹,其實按照紅黑樹的定義沒必要這樣。只要知足紅黑樹的五條性質,就是紅黑樹,好比徹底能夠實現右傾紅黑樹等等,但願你們不要有誤解。
更多關於紅黑樹的知識,好比紅黑樹的插入、刪除操做,限於篇幅,本文再也不介紹,有興趣的仍是推薦你們閱讀《算法4》或者《算法導論》。
更多關於算法、數據機構和計算機基礎知識的內容,歡迎掃碼你們關注個人公衆號「超悅編程」。
超悅編程排序