早上好,我是彤哥。架構
上一節,咱們一塊兒從二叉樹、二叉查找樹、平衡樹、AVL樹、2-3樹、2-3-4樹、B樹,一路講到紅黑樹,最後得出紅黑樹的本質:紅黑樹就是2-3-4樹,請看下圖:ide
咱們知道2-3-4的插入、刪除、查找元素的原理是至關簡單的,那麼,咱們是否是能夠利用2-3-4樹來記憶紅黑樹呢?學習
答案是確定的,本節,咱們就來看看如何利用2-3-4樹來快速掌握紅黑樹,不再用死記硬背了~~3d
好了,讓咱們進入今天的學習吧。code
咱們給出一張圖簡單地回顧一下上一節關於2-3-4樹插入元素N的過程:blog
關注公主號彤哥讀源碼,查看上一節的內容。圖片
在正式講解紅黑樹以前呢,彤哥先來給你們普及幾個有意思的概念,分別是左傾紅黑樹、右傾紅黑樹、AA樹。源碼
圖片過小?試試橫屏!
it
請看上圖,其實按照紅黑樹的概念,上面3顆樹都是紅黑樹,並且元素也是如出一轍,能夠說是同一顆紅黑樹的不一樣變種。class
細心的同窗會發現①和②是同一顆2-3-4樹演化而來,③是這顆2-3-4樹縮小成2-3樹的樣子。
那麼,到底什麼是紅黑樹呢?
紅黑樹是每一個節點都帶有顏色屬性的二叉查找樹,顏色或紅色或黑色。
首先,紅黑樹是一顆二叉查找樹,另外,它還必須知足如下五點要求:
你們不用記這個概念哈,由於確實很難記得住哈,下面彤哥會教你們更簡單的方法。
因此,你看上面三個圖是否是都是紅黑樹呢?
並非啊,由於葉子節點有的是紅色的呀。
其實,它們都是紅黑樹,讓我把葉子節點補齊:
你再仔細看看,是否是知足上面五條規則了?!
因此,你看,隨便畫一顆樹,它均可能知足紅黑樹的定義,所以,爲了方便記憶,咱們將紅黑樹分紅這麼幾種類型:左傾紅黑樹、右傾紅黑樹、AA樹。
左傾紅黑樹(LLRB,Left-Learning Red-Black Tree),一個節點若是有紅色子節點,那麼,它的紅色子節點是向左傾斜的。
怎麼理解呢?
咱們仍是把上面的null節點幹掉哈,葉子節點都是null節點,那是經典紅黑樹的講法,到彤哥這裏,徹底不存在這種要求。
咱們來看,一個節點要麼有一個子節點,要麼有兩個子節點,對吧。
若是這個節點有紅色的子節點呢,也是一個或者兩個,若是隻有一個紅色子節點的話,那麼,這個子節點只能在左邊,若是是有兩個紅色子節點,那就不用管。
因此,整顆紅黑樹中,若是存在紅色節點,那麼只能是下面這兩種形態:
同理,右傾紅黑樹(RLRB,Right-Learning Red-Black Tree),也是同樣的道理,即紅色子節點向右傾斜,它的紅色子節點只能是下面這兩種形態:
好了,左傾和右傾紅黑樹都還算比較正常的形態,還有一種變態的紅黑樹,叫做AA樹(AA Tree)。
固然,這裏的AA不是吃飯的時候你們各付各的哈,這裏的AA是其做者的名字的縮寫:Arne Andersson。
AA樹,是指紅黑樹中全部的紅色子節點必須只能是右節點,左子節點一概不容許是紅色子節點,因此,在AA樹中,紅色子節點只能是下面這一種形態:
也能夠理解爲嚴重右傾主義(我這麼說會不會被約去喝茶^^)。
其實AA樹能夠看做2-3樹的右傾演化而來,而不是2-3-4樹,你能夠畫個圖體驗一下。
好了,上面就是左傾紅黑樹、右傾紅黑樹、AA樹的概念,固然,也有可能存在一種紅黑樹,好比紅色子節點只能是左子節點,是否是叫BB樹,咱也不知道,還有一種多是像下面這種紅黑樹:
上面這顆樹,它既有左邊的紅色子節點,也有右邊的紅色子節點,其實它也知足紅黑樹的定義,這種就只是普通(經典)的紅黑樹了。
既然紅黑樹有這麼多徹底不一樣的形態,咱們要如何快速的記住它們呢?
很難,真的很難,因此,咱們只須要記住一種形態就能夠了,好比左傾紅黑樹,其它的形態都是同樣的道理,徹底不用強形記憶。
所以,下面的內容,我將所有以左傾紅黑樹來說解,跟經典的紅黑樹講法會有點出入,且跟你之前看到的全部文章都不同,請不要糾結。
首先,讓咱們約定一件事:插入的節點必須爲紅色,但若是是根節點,就把它塗成黑色。
有了這個約定以後,咱們使用一步一圖的方式來慢慢拆解紅黑樹(左傾,下同)插入元素的過程。
插入第一個元素F
第一個元素確定是根節點,直接塗成黑色:
插入第二個元素
這裏分兩種狀況:比F小,比F大。
(1)假設插入的元素爲D,那麼,它比F小,因此會成爲F的左子節點:
此時,D爲紅色左子節點,因此,不須要再平衡。
(2)假設插入的元素爲K,那麼,它比F大,因此會成爲F的右子節點:
此時,K爲紅色右子節點,不符合左傾紅黑樹的規則,因此,須要再平衡,那麼,要如何再平衡呢?
讓咱們迴歸紅黑樹的本質——2-3-4樹,上面包含F和K兩個元素的紅黑樹換成2-3-4樹就變成了:
再把這個2-3-4樹轉換成左傾紅黑樹就變成了:
讓咱們畫一張對比圖來看看:
因此,你看,結合2-3-4樹來理解紅黑樹是否是就特別簡單了,對於2-3-4樹就是一個普通的3節點,而對於紅黑樹至關於插入一個右子節點,再作一次左旋變色便可。
插入第三個元素
咱們以上述的F K
兩個元素的紅黑樹爲例,在這個基礎上再增長一個元素,這裏可能有三種狀況,咱們一一來分析:
(1)假設插入的元素爲D,它比F小,因此會成爲F的左子節點:
此時,顯然不符合紅黑樹的定義了,因此,須要再平衡,那麼如何平衡呢?來,上圖:
插入元素D,對於2-3-4樹就是造成一個4節點,而對於紅黑樹樹須要通過右旋再變色的過程。
(2)假設插入的元素爲G,它比F大,比K小,因此會成爲F的右子節點:
顯然,它也不符合紅黑樹的定義,因此,也須要再平衡:
插入元素G,對於2-3-4樹,只是造成一個普通的4節點,而對於紅黑樹,須要先以F左旋,變成與狀況(1)相同的狀態,再以G右旋,而後變色,最終再平衡成紅黑樹。
(3)假設插入的元素爲N,它比K大,因此會成爲K的右子節點:
此時,正好符合紅黑樹的定義,不須要再平衡了,可是,咱們一樣畫一張圖對比看下:
好了,經過上面的分析,連續插入三個元素,能夠看到,對於2-3-4,都是造成一個4節點,而對於紅黑樹,最終都變成了下面這個樣子:
因此,咱們再插入第四個元素看看。
插入第四個元素
咱們以F K N
這顆紅黑樹爲例,插入第四個元素,可能會出現四種狀況,也就是分別可能會成爲F和N的四個子節點的其中之一,簡單點,咱們直接上圖:
(1)假設爲D,其爲F的左子節點
(2)假設爲G,其爲F的右子節點
(3)假設爲M,其爲N的左子節點
(4)假設爲Q,其爲N的右子節點
好了,插入四個元素的各類狀況到此結束,能夠看到,插入第四個元素時,對於2-3-4樹,會造成一個5節點,而後再分裂,而對於紅黑樹,要通過一系列的左旋、右旋、變色,最終轉變成跟2-3-4樹對應的形態,是否是很好玩兒^^
插入第五個元素
畫圖太累,交給你了~~
不論是2-3-4樹仍是左傾紅黑樹刪除元素的過程都要比插入元素複雜得多,咱們先來看2-3-4樹刪除元素的過程。
爲了方便講解,我構造了一顆下圖所示的2-3-4樹:
對於2-3-4樹,刪除3節點或4節點的葉子節點是最簡單的,好比C D
和P Q R
這兩個葉子節點,刪除這兩個節點中的任意一個元素直接刪除便可,4節點刪除一個元素後變成3節點,3節點刪除一個元素以後變成2節點,並不影響原來樹的平衡性,好比,刪除C以後的結果以下:
可是,刪除2節點就不同了,好比,上圖刪除A
、B
、F
、G
、H
、J
、L
、N
這幾個節點,直接刪除以後樹就不平衡了,因此,須要想一些辦法來保證刪除L以後樹依然是平衡的,怎麼辦呢?
答案是——偷!
沒錯,就是偷,從別的地方偷元素過來,把這個空缺補上,就像咱們上班划水同樣,總要找一些東西把工時補上對不對。
那麼,怎麼個偷法呢?
整體來講,分紅兩大類,子節點從父節點偷,父節點從子節點偷,偷着偷着可能還要合併或者遷移元素。
咱們來分別看一下刪除A
、B
、F
、G
、H
、J
、L
、N
這幾個節點的過程是如何偷的,如下多圖,請慎重!
(1)刪除A
刪除A元素時,先從父節點偷個B過來,此時,B位置空缺了,原來B的位置再從其右子節點偷個C過來,搞定。
(2)刪除B
刪除B就很簡單了,直接從右子節點偷個C過來就搞定了。
(3)刪除F
刪除F的過程就比較複雜了,總之,始終圍繞着一個原則:子節點偷不到就偷父節點的,偷過來的元素以後記得可能會合並或者遷移元素。
合併的規則是要始終保證整顆樹的有序性,好比,上面從父節點偷了個I過來,它自己就比H大,因此,H必須放在I的左子節點,而左子節點原來已經有G了,因此,只能把它們倆合併了。
同理,遷移J元素的過程也是同樣的,J確定是要放在K的左邊,遷移到I的右子節點正好。
(4)刪除G
其實跟刪除F時從偷I開始是同樣的,就不贅述了。
(5)刪除H
與刪除F的過程如出一轍,再也不贅述。
(6)刪除J
刪除J時,從父節點先偷個K過來,此時父節點變成了3節點,因此,直接把M左邊的兩個元素合併便可。
(7)刪除L
刪除L的過程與刪除J的過程有點像,也是從父節點偷K過來,而後再把M左邊的兩個元素合併。
(8)刪除N
刪除N時,從父節點偷個O過來,父節點再從其右子節點偷個P過來,偷個屁,偷個屁呀~~
好了,到此爲止,2-3-4樹刪除元素的過程全解析完畢了,我這個示例中幾乎包含了全部的場景,請多畫圖仔細體會,雖然畫得想吐血了。
注:紅黑樹的刪除稍微有點小複雜,若是強型跟2-3-4掛鉤會變得更復雜,因此,下面的內容不徹底跟2-3-4樹掛鉤。
首先,我想問一個問題:一顆二叉查找樹刪除元素以後如何還能保證它仍是二叉查找樹呢?
若是是葉子節點,刪了也就刪了,不影響,但若是是非葉子節點呢?好比,刪除M這個元素。
其實,有兩種方法:一種是找到M的前置節點並拿到M的位置,一種是找到M的後繼節點並拿到M的位置。
什麼是前置節點?什麼是後繼節點呢?好像二叉樹裏面只據說過父節點、子節點?
咱們知道二叉查找樹本質上是有序的,這個有序性指的是元素的天然順序(還有一種有序性是插入順序)。
因此,你把這顆二叉樹中的全部元素排個序(或者中序遍歷一下),在M前面的那個節點就是前置節點,在M後面的那個節點就是後繼節點。
還有一種更形象的方法,M這個節點左子樹中最大的元素就是M的前置節點,M節點右子樹中最小的元素就是M的後繼節點。
因此,刪除M後,把L或者N移到M的位置就能夠了,此時,就能保證二叉查找樹依然是二叉查找樹。
不過,你們好像都喜歡移後繼節點,即右子樹中最小的節點你若是看源碼的話,會看到一個單詞叫做successor
,就是後繼節點的意思。
好了,關於二叉查找樹刪除元素咱們就講這麼多,仍是回到紅黑樹刪除元素的過程。
爲了方便講解,我構造了下面這麼一顆紅黑樹:
咱們先來看一種最簡單的狀況,若是刪除的是紅色的葉子節點,好比,上圖中的C、P、R這三個元素,若是它的父節點只有它這麼一個子節點,直接刪之,啥也不用管,好比C,若是它的父節點有兩個子節點,那麼會分紅兩種狀況,一種是刪除的右子節點,則直接刪,好比R,另外一種是刪除的左子節點,那就作一次簡單的左旋便可,好比P。
咱們這裏講的是左傾紅黑樹,若是是經典的紅黑樹,則刪除紅色葉子節點不須要旋轉。
OK,咱們再來看第二種狀況,若是刪除的是黑色的葉子節點呢?
咱們知道,黑色節點刪除以後,確定不符合紅黑樹定義了,因此,確定要進行再平衡的過程。
若是按照經典紅黑樹的說法,要看它的兄弟節點的顏色,有可能還要看它兄弟節點的子節點的顏色,狀況大概有三四種,根本不可能記得住,我這裏介紹一種更牛逼的方法,保證你看一遍就能記住。
咱們以刪除F節點爲例,我先給出圖示,下面再描述詳細步驟:
這種方法很是簡單,F是黑色節點沒錯,那就想辦法把它變成紅色節點,怎麼變呢?
那就得從它的上層節點動手,上層節點的紅色實際上是能夠向下傳遞的,傳遞以後,整顆樹其實仍是紅黑樹,並不會打破原來紅黑樹的平衡,直到F變成紅色的葉子節點,再一舉把它刪除,就很簡單了。
這種方法相比於經典紅黑樹的方法,理解起來就容易得多了。
咱們再舉個刪除L的例子,直接上圖:
好了,上面說的都是刪除葉子節點,那麼,若是刪除的是非葉子節點呢,好比刪除E。
根據二叉查找樹的特性,那麼,咱們會找到E的後繼節點F,而後,把它移到E的位置,可是,此時,不符合紅黑樹的定義了,因此,你能夠發現,其實,刪除E至關於間接地刪除F原來所在的節點位置,所以,又轉化成了上面的刪除葉子節點。
過程很簡單,最後的結果與刪除F的結果基本相同,只是原來E所在位置的元素變成了F,我就不畫圖了。
你能夠想一想刪除M的過程~
總算講完了,能看到這裏的同窗不容易,可能已經超出了一頓早餐的時間,我很抱歉!
本節,咱們從紅黑樹的本質,即2-3-4樹出發,完全掌握了一種不用死記硬背的方法來理解紅黑樹,你Get到了嗎?歡迎留言評論。
有些同窗看到這裏,可能又說了:Talk is cheap, show me the code!
好,下一節,我就show you the code,敬請期待!
關注公主號「彤哥讀源碼」,解鎖更多源碼、基礎、架構知識。