今天咱們要說的紅黑樹就是就是一棵非嚴格均衡的二叉樹,均衡二叉樹又是在二叉搜索樹的基礎上增長了自動維持平衡的性質,插入、搜索、刪除的效率都比較高。紅黑樹也是實現 TreeMap 存儲結構的基石。性能
二叉搜索樹又叫二叉查找樹、二叉排序樹,咱們先看一下典型的二叉搜索樹,這樣的二叉樹有何規則特色呢?學習
二叉搜索樹有以下幾個特色:spa
下圖就是一棵典型的二叉搜索樹:3d
在這裏插入圖片描述
二叉搜索樹是均衡二叉樹的基礎,咱們看一下它的搜索步驟如何。咱們要從二叉樹中找到值爲 58 的節點。blog
第一步:首先查找到根節點,值爲 60 的節點。排序
在這裏插入圖片描述
第二步:比較咱們要找的值 58 與該節點的大小。教程
若是等於,那麼恭喜,已經找到;若是小於,繼續找左子樹;若是大於,那麼找右子樹。圖片
很明顯 58<60,所以咱們找到左子樹的節點 56,此時咱們已經定位到了節點 56。 資源
在這裏插入圖片描述it
第三步:按照第二步的規則繼續找。
58>56 咱們須要繼續找右子樹,定位到了右子樹節點 58,恭喜,此時咱們已經找到了。
在這裏插入圖片描述
咱們通過三步就已經找到了,其實就是咱們平時所說的二分查找,這種二叉搜索樹好像查找效率很高,但一樣它也有缺陷,以下面這樣的二叉搜索樹。
在這裏插入圖片描述
看到這樣的二叉搜索樹是否很彆扭,典型的大長腿瘸子,但它也是二叉搜索樹,若是咱們要找值爲 50 的節點,基本上和單鏈表查詢沒多大區別了,性能將大打折扣。
這個時候咱們的均衡二叉樹就粉墨登場了,均衡二叉樹就是在二叉搜索樹的基礎上添加了自動維持平衡的性質。
上面的大長腿瘸子二叉搜索樹通過自動平衡後,可能就成爲了下面這樣的二叉樹。
在這裏插入圖片描述
通過了自動平衡,再去找值爲 50 的節點,查找性能將提高不少。紅黑樹就是非嚴格均衡的二叉搜索樹。
紅黑樹具體有哪些規則特色呢?具體以下:
**1. 節點分爲紅色或者黑色。
規則看着好像挺多,沒錯,由於紅黑樹也是均衡二叉樹,須要具有自動維持平衡的性質,上面的 6 條就是紅黑樹給出的自動維持平衡所須要具有的規則。
咱們看一看一個典型的紅黑樹究竟是什麼樣兒?
在這裏插入圖片描述
首先解讀一下規則,除了字面上看到的意思,還隱藏了哪些意思呢?
①從根節點到葉子節點的最長路徑不大於最短路徑的 2 倍
怎麼樣的路徑算最短路徑?從規則 5 中,咱們知道從根節點到每一個葉子節點的黑色節點數量是同樣的,那麼純由黑色節點組成的路徑就是最短路徑。
什麼樣的路徑算是最長路徑?根據規則 4 和規則 3,如有紅色節點,則必然有一個鏈接的黑色節點,當紅色節點和黑色節點數量相同時,就是最長路徑,也就是黑色節點(或紅色節點)*2。
②爲何說新加入到紅黑樹中的節點爲紅色節點
從規則 4 中知道,當前紅黑樹中從根節點到每一個葉子節點的黑色節點數量是同樣的,此時假如新的是黑色節點的話,必然破壞規則。
但加入紅色節點卻不必定,除非其父節點就是紅色節點,所以加入紅色節點,破壞規則的可能性小一些,下面咱們也會舉例來講明。
什麼狀況下,紅黑樹的結構會被破壞呢?破壞後又怎麼維持平衡,維持平衡主要經過兩種方式【變色】和【旋轉】,【旋轉】又分【左旋】和【右旋】,兩種方式可相互結合。
下面咱們從插入和刪除兩種場景來舉例說明。
當咱們插入值爲 66 的節點時,紅黑樹變成了這樣:
在這裏插入圖片描述
很明顯,這個時候結構依然遵循着上述 6 大規則,無需啓動自動平衡機制調整節點平衡狀態。
若是再向裏面插入值爲 51 的節點,這個時候紅黑樹變成了這樣:
在這裏插入圖片描述
很明顯如今的結構不遵循規則 4 了,這個時候就須要啓動自動平衡機制調整節點平衡狀態。
咱們能夠經過變色的方式,使結構知足紅黑樹的規則:
**1. 首先解決結構不遵循規則 4 這一點(紅色節點相連,節點 49-51),需將節點 49 改成黑色。
最終調整完成後的樹爲:
在這裏插入圖片描述
但並非何時都那麼幸運,能夠直接經過變色就達成目的,大多數時候還須要經過旋轉來解決。
如在下面這棵樹的基礎上,加入節點 65:
在這裏插入圖片描述
插入節點 65 後進行如下步驟:
在這裏插入圖片描述
這個時候,你會發現對於節點 64 不管是紅色節點仍是黑色節點,都會違反規則 5,路徑中的黑色節點始終沒法達成一致,這個時候僅經過【變色】已經沒法達成目的。
咱們須要經過旋轉操做,固然【旋轉】操做通常還須要搭配【變色】操做。旋轉包括【左旋】和【右旋】。
左旋:逆時針旋轉兩個節點,讓一個節點被其右子節點取代,而該節點成爲右子節點的左子節點。
左旋操做步驟以下:首先斷開節點 PL 與右子節點 G 的關係,同時將其右子節點的引用指向節點 C2;而後斷開節點 G 與左子節點 C2 的關係,同時將 G 的左子節點的應用指向節點 PL。
在這裏插入圖片描述
右旋:順時針旋轉兩個節點,讓一個節點被其左子節點取代,而該節點成爲左子節點的右子節點。
右旋操做步驟以下:首先斷開節點 G 與左子節點 PL 的關係,同時將其左子節點的引用指向節點 C2;而後斷開節點 PL 與右子節點 C2 的關係,同時將 PL 的右子節點的應用指向節點 G。
沒法經過變色而進行旋轉的場景分爲如下四種:
這種狀況下,父節點和插入的節點都是左節點,以下圖(旋轉原始圖1)這種狀況下,咱們要插入節點 65。
規則以下:以祖父節點【右旋】,搭配【變色】。
在這裏插入圖片描述
按照規則,步驟以下:
在這裏插入圖片描述
這種狀況下,父節點是左節點,插入的節點是右節點,在旋轉原始圖 1 中,咱們要插入節點 67。
規則以下:先父節點【左旋】,而後祖父節點【右旋】,搭配【變色】。
按照規則,步驟以下:
在這裏插入圖片描述
這種狀況下,父節點是右節點,插入的節點是左節點,以下圖(旋轉原始圖 2)這種狀況,咱們要插入節點 68。
規則以下:先父節點【右旋】,而後祖父節點【左旋】,搭配【變色】。
在這裏插入圖片描述
按照規則,步驟以下:
在這裏插入圖片描述
這種狀況下,父節點和插入的節點都是右節點,在旋轉原始圖 2 中,咱們要插入節點 70。
規則以下:以祖父節點【左旋】,搭配【變色】。
按照規則,步驟以下:
在這裏插入圖片描述
紅黑樹插入總結:
在這裏插入圖片描述
相比較於紅黑樹的節點插入,刪除節點更爲複雜,咱們從子節點是否爲 null 和紅色爲思考維度來討論。
當待刪除的節點的子節點至少有一個爲 null 節點時,刪除了該節點後,將其有值的節點取代當前節點便可。
若都爲 null,則將當前節點設置爲 null,固然若是違反規則了,則按需調整,如【變色】以及【旋轉】。
在這裏插入圖片描述
這種狀況下,第一步:找到該節點的前驅或者後繼。
前驅:左子樹中值最大的節點(可得出其最多隻有一個非 null 子節點,可能都爲 null)。
後繼:右子樹中值最小的節點(可得出其最多隻有一個非 null 子節點,可能都爲 null)。
前驅和後繼都是值最接近該節點值的節點,相似於該節點.prev=前驅,該節點.next=後繼。
第二步:將前驅或者後繼的值複製到該節點中,而後刪掉前驅或者後繼。
若是刪除的是左節點,則將前驅的值複製到該節點中,而後刪除前驅;若是刪除的是右節點,則將後繼的值複製到該節點中,而後刪除後繼。
這至關因而一種「取巧」的方法,咱們刪除節點的目的是使該節點的值在紅黑樹上不存在。
所以專一於該目的,咱們並不關注刪除節點時是否真是咱們想刪除的那個節點,同時咱們也不需考慮樹結構的變化,由於樹的結構自己就會由於自動平衡機制而常常進行調整。
前面咱們已經說了,咱們要刪除的其實是前驅或者後繼,所以咱們就之前驅爲主線來說解。
後繼的學習可參考前驅,包括下面幾種狀況:
①前驅爲黑色節點,而且有一個非 null 子節點
在這裏插入圖片描述
分析:由於要刪除的是左節點 64,找到該節點的前驅 63;而後用前驅的值 63替換待刪除節點的值 64,此時兩個節點(待刪除節點和前驅)的值都爲 63;
刪除前驅 63,此時成爲上圖過程當中間環節,但咱們發現其不符合紅黑樹規則 4,所以須要進行自動平衡調整。這裏直接經過【變色】便可完成。
②前驅爲黑色節點,同時子節點都爲 null
在這裏插入圖片描述
分析:由於要刪除的是左節點 64,找到該節點的前驅 63;而後用前驅的值 63 替換待刪除節點的值 64,此時兩個節點(待刪除節點和前驅)的值都爲 63。
刪除前驅 63,此時成爲上圖過程當中間環節,但咱們發現其不符合紅黑樹規則 5,所以須要進行自動平衡調整。這裏直接經過【變色】便可完成。
③前驅爲紅色節點,同時子節點都爲 null
在這裏插入圖片描述
分析:由於要刪除的是左節點 64,找到該節點的前驅 63;而後用前驅的值 63替換待刪除節點的值 64,此時兩個節點(待刪除節點和前驅)的值都爲 63;刪除前驅 63,樹的結構並無打破規則。
紅黑樹刪除的狀況比較多,但也就存在如下狀況:
本文主要介紹了紅黑樹的相關原理,首先紅黑樹的基礎二叉搜索樹,咱們先簡單說了一下二叉搜索樹,而且講了一下搜索的流程。
而後就針對紅黑樹的六大規則特色,紅黑樹的插入操做,刪除操做,都使用了大量的圖形來加以說明。
技術都是練出來的,有時候不少似是而非的地方,當動筆去寫的時候,其實很好理解。
紅黑樹的使用很是普遍,如 TreeMap 和 TreeSet 都是基於紅黑樹實現的,而 JDK8 中 HashMap 當鏈表長度大於 8 時也會轉化爲紅黑樹。
更多資源和教程請關注公衆號:非科班的科班。
若是以爲我寫的還能夠請給個贊,謝謝你們,你的鼓勵是我創做的動力