要掌握紅黑樹?仍是得理解紅黑樹的刪除跟插入過程,理解紅黑樹的操做來源!

引言

預防針:紅黑樹原本就是基本算法中的難點,因此看此文時建議先有點預備心理或知識鋪墊,沒接觸過RBT而直接看此文的話,絕對懵逼。面試

爲了數據的查詢跟增刪方便,系統引入了二叉查找樹,它具備左節點 < 跟節點 <右節點 的屬性,算法

二叉查找樹,可是這種設定跟數據的插入順序有很大關係,好比你插入的是1234,二叉查找樹會退化爲鏈表。markdown

鏈表,爲了不鏈表結構的出現,研究者們又提出了平衡二叉樹跟紅黑樹。平衡二叉樹要求任意一個節點的左深度跟右深度差值絕對值不能大於1,若是插入後超過了1會經過左右各類旋轉來更改鏈接的變化,最終實現左右深度差不大於1的這個要求。數據結構

平衡二叉樹的深度要求太過完美,當涉及大量增刪時,可能會太多時間用在調節平衡上,爲了平衡投入跟產出比,又設計了紅黑樹。oop

紅黑樹算是一個比較複雜的數據結構了,除非你面字節,可能讓你手寫紅黑樹。通常狀況下你只要說出紅黑樹構造的五大背後邏輯,展示你對底層數據結構的深度跟廣度便可。性能

本文不會着重說紅黑樹的增刪過程,由於你百度看下權威教程或源碼,而後跟着追蹤就知道大體流程了,本文會說下紅黑樹爲什麼如此設計,它跟2-3樹有啥聯繫。學習

定義

爲了保證查找樹的平衡性,須要一些靈活性,所以咱們容許樹中的一個結點保存多個鍵。spa

2結點:含有一個鍵和兩條連接,左連接指向的2-3樹中的鍵都小於該結點,右連接指向的2-3樹中的鍵都大於該結點。設計

3結點:含有兩個鍵和三條連接,左連接指向的2-3樹中的鍵都小於該結點,中連接指向的2-3樹中的鍵都位於該結點的兩個鍵之間,右連接指向的2-3樹中的鍵都大於該結點。3d

4節點:含有三個鍵和四條連接,大體的思路跟3節點相似。需注意在2-3樹中,4節點是短暫存在的,會被轉化爲2節點或3節點。

查找

要判斷一個鍵是否在樹中,咱們先將它和根結點中的鍵比較。若是它和其中的任何一個相等,查找命中。不然咱們就根據比較的結果找到指向相應區間的連接,並在其指向的子樹中遞歸地繼續查找。若是這是個空連接,查找未命中,能夠發現跟簡單的二叉樹查找相似。

插入

要在2-3樹中插入一個新結點,咱們能夠和二叉查找樹同樣先進行一次未命中的查找,而後把新結點掛在樹的底部。但這樣的話樹沒法保持完美平衡性。使用2-3樹的主要緣由就在於它可以在插入以後繼續保持平衡。

若是未命中的查找結束於一個2結點:只要把這個2結點替換爲一個3結點,將要插入的鍵保存在其中便可。 只有一個3結點的樹,向其插入一個新數據:此時咱們能夠建立個臨時4節點,而後將其轉化爲由3個2節點組成的2-3樹

只有3節點樹插入數據

向一個父結點爲2結點的3結點中插入新鍵:此時先將組成個臨時4節點,而後將中間數提到上面跟父節點融合爲一個3節點,這樣樹的高度沒變。

向一個父結點爲3結點的3結點中插入新鍵4:跟上面套路相似,不斷將中位數的數據往上提,直到遇到個2節點,或者到達了根節點而後進行拆分。

![(p3-juejin.byteimg.com/tos-cn-i-k3…)

插入總結:

先找插入結點,若結點是2結點,則直接插入。如結點3結點,則插入使其臨時容納這個元素,而後分裂此結點,把中間元素移到其父結點中。對父結點亦如此處理。(中鍵一直往上移,直到找到空位,在此過程當中沒有空位就先搞個臨時的,再分裂。) 2-3樹插入算法的根本在於這些變換都是局部的:除了相關的結點和連接以外沒必要修改或者檢查樹的其餘部分。每次變換中,變動的連接數量不會超過一個很小的常數。全部局部變換都不會影響整棵樹的有序性和平衡性。

刪除

2-3樹的刪除分爲兩種狀況。

若是待刪除元素在3節點,那麼能夠直接將這個元素刪除,刪除這個元素不會引發高度的變化。

刪除3節點中數據

當待刪除元素在2節點時,因爲刪除這個元素會致使2節點失去惟一的元素,引起樹中某條路徑的高度發生變化,爲維持平衡,此時有兩種方法。

先刪除再對2-3樹進行平衡調整。

想辦法讓這個被刪除的元素不可能出如今2節點中。若是發現刪除元素樹2節點則會從兄弟節點或父節點借個元素,當前2節點變爲3節點或臨時4節點,而後再刪除目標數據。

2節點狀況下刪除目標數據2

構造

和標準的二叉查找樹由上向下生長不一樣,2-3樹的生長是由下向上的。

插入

優勢、缺點

優勢:

2-3樹在最壞狀況下仍有較好的性能。每一個操做中處理每一個結點的時間都不會超過一個很小的常數,且這兩個操做都只會訪問一條路徑上的結點,因此任何查找或者插入的成本都確定不會超過對數級別。

完美平衡的2-3樹要平展的多。例如含有10億個結點的一顆2-3樹的高度僅在19到30之間。咱們最多隻須要訪問30個結點就能在10億個鍵中進行任意查找和插入操做。

缺點:

咱們須要維護兩種不一樣類型的結點,查找和插入操做的實現須要大量的代碼,並且它們所產生的額外開銷可能會使算法比標準的二叉查找樹更慢。

平衡一棵樹的初衷是爲了消除最壞狀況,但咱們但願這種保障所需的代碼可以越少越好,越簡單越好,顯然2-3樹也不太適合。

既然已經懂了2-3樹的實現,接下來咱們對2-3樹稍微變型下就是紅黑樹了,你能夠認爲紅黑樹的本質實際上是對概念模型2-3-4樹的一種實現。

紅黑樹

樹跟紅黑樹關聯

因爲直接進行不一樣節點間的轉化會形成較大的開銷,因此選擇以二叉樹爲基礎,在二叉樹的屬性中加入一個顏色屬性來表示2-3樹中不一樣的節點。

2-3樹中的2節點對應着紅黑樹中的黑色節點。

2-3樹中的非2節點是以紅節點 + 黑節點的方式存在,紅節點的意義是與黑色父節點結合,表達着2-3樹中的3,4節點。有的書上把紅色說成了紅色連接,也是一直理解方法。

先看2-3樹到紅黑樹的節點轉換。2節點直接轉化爲黑色節點。3節點這裏能夠有兩種表現形式,左傾紅節點或者右傾紅節點。而4節點被強制要求轉化爲一個黑父帶着左右兩個紅色兒子。

23樹到紅黑樹轉變 因爲3節點轉化到時候能夠左傾也能夠右傾,若是查看算法書籍,你會發現爲了簡單化,算法書籍中統一規定只用左傾紅黑樹。

紅黑樹跟2-3樹轉化到時候,能夠認爲將紅色節點順時針上升45度,來跟它到父節點保持平衡,再將紅色到跟父節點看做一個總體。

紅黑樹轉2-3樹,能夠發現黑色節點纔會真正在2-3樹中增長高度,因此紅黑樹的完美平衡其實等價2-3樹的根節點到葉子節點到距離相等。因此說紅黑樹是2-3樹或2-3-4樹概念模型的一種實現。

在算法導論中紅黑樹樹基於2-3-4樹實現的。

在算法4中紅黑樹樹基於2-3樹實現的,而且要求3節點在紅黑樹中必須以左傾紅色節點來表示。

2-3樹確定比2-3-4樹簡單,因此接下來主要基於2-3樹說。

紅黑樹基礎定義跟旋轉

五大法則

節點有黑色跟紅色兩種:至於爲什麼有紅色節點,在2-3樹中已經說過了。

根節點必須是黑色:2-3樹中若是根節點樹2節點那原本就是黑色,若是是3節點就用大的當黑根節點,小的當左傾紅節點。

葉子節點爲不存數據且都是黑色:主要是爲了在插入跟刪除時候方便操做。

任意節點到葉子節點通過的黑色節點數目相同:紅黑樹中的紅節點是和黑色父節點綁定的,在2-3樹中原本就是同一層的,只有黑色節點纔會在2-3樹中真正貢獻高度,因爲2-3樹的任一節點到空連接距離相同,所以反應在紅黑樹中就是黑色完美平衡。

不會有連續的紅色節點:2-3樹中原本就規定沒有4節點,2-3-4樹中雖然有4節點,可是要求在紅黑樹中體現爲一黑色節點帶兩紅色兒子,分佈左右,因此也不會有連續紅節點。

左旋跟右旋

紅黑樹要求新插入數據顏色是紅色,黑色是改變後的結果。紅黑樹的核心是左旋跟右旋。

左旋跟右旋

左傾紅黑樹插入

左傾紅黑樹的插入一共有三種可能的狀況。

待插入元素比黑父大,插在了黑父的右邊,而黑父左邊是紅色兒子。這種狀況會致使在紅黑樹中出現右傾紅節點。或者黑父左邊爲空也會出現右傾。

插入20

待插入元素比紅父小,且紅父自身就是左傾,待插入數據比紅父左節點還小,造成了連續的紅節點。

對紅父的父親節點進行一次右旋轉。

將數據變化爲狀況1的狀態處理。

image.png

插入14

待插入元素比紅父大,且紅父自身就是左傾。待插入數據比紅父左節點大,造成了右傾。經過左旋變成狀況2處理。

插入17 總體來講左傾紅黑樹的插入就是這3種狀況來回切換,最終達到平衡。

左傾紅黑樹刪除

刪除思路是不刪除目標數據,而是找到目標數據的前驅節點或後繼節點,而後把數據拷貝一份到目標數據進行覆蓋。而後轉而去刪除前驅或後繼。刪除後再去修補平衡。

從宏觀上來看從根節點開始查找,全程利用2-3樹思惟逐層對紅黑樹調整,每次保證當前節點樹2-3樹中非2節點,若是是非2節點則看下一層,若是是2節點則根據兄弟節點調整。

兄弟節點是2節點,從父節點借個數據跟當前節點及兄弟節點造成臨時4節點。

兄弟節點是非2節點,兄弟節點上升一個數據,父節點降低一個數據。

刪除目標1 刪除後就涉及到數據平衡修復了,仍是根據2-3樹來修復平衡,路上可能會碰到紅色右傾節點,遇到就進行一次左旋便可。

2-3樹修補工做

工業級紅黑樹增長

這裏其實主要參考極客時間小爭哥的文章,說下實際工程中紅黑樹的增刪操做,增長主要有3種狀況:

狀況1:關注節點是 a,它的叔叔節點 d 是紅色:

將關注節點 a 的父節點 b、叔叔節點 d 的顏色都設置成黑色。

將關注節點 a 的祖父節點 c 的顏色設置成紅色。

關注節點變成 a 的祖父節點 c,實現關注節點的遷移。

跳到狀況2或狀況3。

image.png

狀況2:關注節點是 a,它的叔叔節點 d 是黑色,關注節點 a 是其父節點 b 的右子節點:

關注節點變成節點 a 的父節點 b。

圍繞新的關注節點 b 左旋。

跳到狀況3。

image.png

狀況3:若是關注節點是 a,它的叔叔節點 d 是黑色,關注節點 a 是其父節點 b 的左子節點,咱們就依次執行下面的操做:

圍繞關注節點 a 的祖父節點 c 右旋。

將關注節點 a 的父節點 b、兄弟節點 c 的顏色互換,調整結束。

工業級紅黑樹刪除

相比插入,刪除就難多了!核心思想是找準關注點,根據關注點跟周圍節點排布特徵按照必定規則調整。主要倆步驟:

針對刪除節點調整後仍要知足節點到葉子節點路徑包含相同黑色節點。

針對關注節點二次調整,防止出現2個紅色節點。

算法導論中說若是刪除黑節點X帶來黑色平衡破壞,讓X的子節點變爲黑-黑或紅-黑。意思是既然刪除了某個黑色節點,那麼必然會破壞以這個黑色節點爲路徑上的黑色平衡,表現爲路徑中缺乏一個黑,因此要想辦法補充一個黑色節點(下面會用黑色圓圈表示)。同時若是一個節點既能夠紅又能夠黑,就用紅黑兩個組成部分表示。

刪除第一步

狀況1:要刪除的節點是 a,它只有一個子節點 b:

刪除節點 a,而且把節點 b 替換到節點 a 的位置,這一部分操做跟普通的二叉查找樹的刪除操做同樣。

節點 a 只能是黑色,節點 b 也只能是紅色,其餘狀況均不符合紅黑樹的定義。此時把節點 b 改成黑色。調整結束,不須要進行二次調整。

狀況2:要刪除的節點 a 有兩個非空子節點,而且它的後繼節點就是節點 a 的右子節點 c:

若是節點 a 的後繼節點就是右子節點 c,那 c 確定沒有左子樹。將c的顏色變爲a的顏色,而且用c來覆蓋a。

若是節點 c 是黑色,爲了避免違反紅黑樹的路徑相同原則,給節點 c 的右子節點 d 多加一個黑色圓圈,這個時候節點 d 就成了紅 - 黑或者黑 - 黑。

此時關注節點變成了節點 d,第二步的調整操做就會針對關注節點來作。

狀況3:要刪除的是節點 a,它有兩個非空子節點,而且節點 a 的後繼節點不是a的右子節點:

找到後繼節點 d,並將它刪除,刪除後繼節點 d 的過程參照 CASE 1。

用d來替換a,而且d的顏色設置的跟a顏色同樣。

若是節點 d 是黑色,爲了避免違反紅黑樹路徑相同原則,給節點 d 的右子節點 c 多加一個黑色,這個時候節點 c 就成了紅 - 黑或者黑 - 黑。

此時關注節點變成了節點 c,第二步的調整操做就會針對關注節點來作。

刪除第二步

通過初步調整以後,關注節點變成了紅 - 黑或者黑 - 黑    節點。針對這個關注節點,再分四種狀況來進行二次調整。二次調整是爲了讓紅黑樹中不存在相鄰的紅色節點。

i

狀況1:關注節點是 a,它的兄弟節點 c 是紅色的,咱們就依次進行下面的操做:

圍繞關注節點 a 的父節點 b 左旋。

關注節點 a 的父節點 b 和祖父節點 c 交換顏色。

關注節點不變,繼續從四種狀況中選擇適合的規則來調整。

狀況2:關注節點是 a,它的兄弟節點 c 是黑色,而且節點 c 的左右子節點 d、e 都是黑色:

將關注節點 a 的兄弟節點 c 的顏色變成紅色,由於接下來黑圓圈會上移,那麼c比a多個深色。

從關注節點 a 中去掉一個黑色,此時節點 a 就是單純的紅色或者黑色。

給關注節點 a 的父節點 b 添加一個黑色,這個時候節點 b 就變成紅 - 黑或者黑 - 黑

關注節點從 a 變成其父節點 b,繼續從四種狀況中選擇符合的規則來調整。

狀況3:關注節點是 a,它的兄弟節點 c 是黑色,c 的左子節點 d 是紅色,c 的右子節點 e 是黑色:

圍繞關注節點 a 的兄弟節點 c 右旋。

節點 c 和節點 d 交換顏色。

關注節點不變,跳轉到狀況4,繼續調整。

狀況4:關注節點 a 的兄弟節點 c 是黑色的,而且 c 的右子節點是紅色的,咱們就依次進行下面的操做:

圍繞關注節點 a 的父節點 b 左旋。

將b的顏色複製給c,由於c替代了b的位置。

將關注節點 a 的父節點 b 的顏色設置爲黑色。

從關注節點 a 中去掉一個黑色,節點 a 就變成了單純的紅色或黑色。

將關注節點 a 的叔叔節點 e 設置爲黑色,調整結束。

此時a跟d深度是同樣的,由於沒法判別ad是否爲紅,直接將b設置爲黑的了,此時e提升了一度爲保持平衡也設置爲黑色的了。

刪除理解

多畫圖,不畫圖單看代碼一下子就眩暈了。

插入跟刪除算法都是用到了遞推,好比插入狀況1,狀況1的處理以後,關注節點從自己變成了它的祖父紅色節點,這就是往根節點遞推。不過狀況1處理過一次以後,不必定會進入狀況2或者狀況3,有可能還在狀況1。在狀況1的狀況下一直往根節點走,由於當前節點永遠是紅色,因此在最後要把根節點塗黑。同時只要進入到狀況二、狀況3狀況,操做就跟上面說過的相似了。

要記住,除了關注的節點所在的子樹,其餘的子樹自己都是一顆紅黑樹,它們是知足紅黑樹的全部特徵的。當關注節點往根節點遞推時,這個時候關注節點的子樹也已經知足了紅黑樹的定義,咱們就不用再去特別關注子樹的特徵。只要注意關注節點往上的部分。這樣就能把問題簡化,思考的時候思路會清晰一些。

再說到刪除算法,注意紅-黑跟黑-黑存在的緣由,爲什麼最終都會走到從兄弟節點的地方作文章來實現最終的平衡。

刪除狀況1的目的只是爲了可以進入接下來的三個狀況中。

刪除狀況2的套路又是一個遞推思路,關注節點往根節點遞推,讓其左右子樹都知足紅黑樹的定義。由於往上推,右子樹多了一個黑色節點,就把關注節點的兄弟節點變紅。

刪除狀況3是爲了進入刪除狀況4,提早變色的緣由和狀況2是同樣的,都是爲了知足黑色深度相同。一樣是概括推理的思路。都要記住一點,各類狀況下的其餘子樹節點都知足紅黑樹的定義,須要分類討論的,都在這幾種狀況中了。

可能你看今天看了紅黑樹的刪除你頓悟了,過了半個月又迷糊了。不要怕!由於怕也沒用,再看唄。學習紅黑樹自己也不是爲了面試字節去默寫,而是去學習思想,鍛鍊思惟,複雜問題簡單化,固然了順帶也能夠裝的一手好B。

總結

本文的重點不在於講解工業化紅黑樹的刪除跟插入所有過程,只是但願經過2-3樹跟左傾紅黑樹的增刪,讓你們從本質上理解下紅黑樹的操做來源。其中工業化刪除部分已徵得小爭哥贊成,若是理解了上面的內容,那麼你再去看工業化紅黑樹的操做就手到擒來了。

相關文章
相關標籤/搜索