“紅黑樹”詳解丨紅黑樹的應用場景

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"今天我們要說的紅黑樹就是就是一棵非嚴格均衡的二叉樹,均衡二叉樹又是在二叉搜索樹的基礎上增加了自動維持平衡的性質,插入、搜索、刪除的效率都比較高。紅黑樹也是實現 TreeMap 存儲結構的基石。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"文章相關視頻講解:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://www.bilibili.com/video/BV1hp4y1B7Uf/","title":null,"type":null},"content":[{"type":"text","text":"紅黑樹在linux中的3種應用場景,聽完受益匪淺","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://ke.qq.com/course/417774?flowToken=1013189","title":null,"type":null},"content":[{"type":"text","text":"5種紅黑樹的場景,從Linux內核談到Nginx源碼,聽完醍醐灌頂","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://ke.qq.com/course/417774?flowToken=1013189","title":null,"type":null},"content":[{"type":"text","text":"c/c++Linux後臺服務器開發高級架構師視頻資料","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"二叉搜索樹","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"二叉搜索樹又叫二叉查找樹、二叉排序樹,我們先看一下典型的二叉搜索樹,這樣的二叉樹有何規則特點呢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"二叉搜索樹有如下幾個特點:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"節點的左子樹小於節點本身","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"節點的右子樹大於節點本身","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"左右子樹同樣爲二叉搜索樹","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下圖就是一棵典型的二叉搜索樹:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/6a/6a21c5c9366012e660241b9f667b064b.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 二叉搜索樹是均衡二叉樹的基礎,我們看一下它的搜索步驟如何。我們要從二叉樹中找到值爲 58 的節點。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"第一步:","attrs":{}},{"type":"text","text":"首先查找到根節點,值爲 60 的節點。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1f/1fce5ac848924ccb6bd798cb849cf26f.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"第二步:","attrs":{}},{"type":"text","text":"比較我們要找的值 58 與該節點的大小。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果等於,那麼恭喜,已經找到;如果小於,繼續找左子樹;如果大於,那麼找右子樹。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"很明顯 58<60,因此我們找到左子樹的節點 56,此時我們已經定位到了節點 56。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/6b/6b14a6cd84992a97b00caf8a563345df.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":" 第三步:","attrs":{}},{"type":"text","text":"按照第二步的規則繼續找。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"58>56 我們需要繼續找右子樹,定位到了右子樹節點 58,恭喜,此時我們已經找到了。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f4/f4f19ac64268a252a5b319534e8f65c8.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們經過三步就已經找到了,其實就是我們平時所說的二分查找,這種二叉搜索樹好像查找效率很高,但同樣它也有缺陷,如下面這樣的二叉搜索樹。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/2c/2c5acd52e3a3161d590b8b39e0672f8b.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看到這樣的二叉搜索樹是否很彆扭,典型的大長腿瘸子,但它也是二叉搜索樹,如果我們要找值爲 50 的節點,基本上和單鏈表查詢沒多大區別了,性能將大打折扣。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個時候我們的均衡二叉樹就粉墨登場了,均衡二叉樹就是在二叉搜索樹的基礎上添加了自動維持平衡的性質。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面的大長腿瘸子二叉搜索樹經過自動平衡後,可能就成爲了下面這樣的二叉樹。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/60/60f3e1f60f6c70191b7f6bd370719e2c.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"經過了自動平衡,再去找值爲 50 的節點,查找性能將提升很多。紅黑樹就是非嚴格均衡的二叉搜索樹。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"紅黑樹規則特點","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"紅黑樹具體有哪些規則特點呢?","attrs":{}},{"type":"text","text":"具體如下:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"節點分爲紅色或者黑色。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根節點必爲黑色。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"葉子節點都爲黑色,且爲 null。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"連接紅色節點的兩個子節點都爲黑色(紅黑樹不會出現相鄰的紅色節點)。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從任意節點出發,到其每個葉子節點的路徑中包含相同數量的黑色節點。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"新加入到紅黑樹的節點爲紅色節點。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"規則看着好像挺多,沒錯,因爲紅黑樹也是均衡二叉樹,需要具備自動維持平衡的性質,上面的 6 條就是紅黑樹給出的自動維持平衡所需要具備的規則。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們看一看一個典型的紅黑樹到底是什麼樣兒?","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/de/ded708d22e316e5da7a4f3b381de3125.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 首先解讀一下規則,除了字面上看到的意思,還隱藏了哪些意思呢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"①從根節點到葉子節點的最長路徑不大於最短路徑的 2 倍","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"怎麼樣的路徑算最短路徑?從規則 5 中,我們知道從根節點到每個葉子節點的黑色節點數量是一樣的,那麼純由黑色節點組成的路徑就是最短路徑。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"什麼樣的路徑算是最長路徑?根據規則 4 和規則 3,若有紅色節點,則必然有一個連接的黑色節點,當紅色節點和黑色節點數量相同時,就是最長路徑,也就是黑色節點(或紅色節點)*2。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"②爲什麼說新加入到紅黑樹中的節點爲紅色節點","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從規則 4 中知道,當前紅黑樹中從根節點到每個葉子節點的黑色節點數量是一樣的,此時假如新的是黑色節點的話,必然破壞規則。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但加入紅色節點卻不一定,除非其父節點就是紅色節點,因此加入紅色節點,破壞規則的可能性小一些,下面我們也會舉例來說明。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"什麼情況下,紅黑樹的結構會被破壞呢?","attrs":{}},{"type":"text","text":"破壞後又怎麼維持平衡,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"維持平衡主要通過兩種方式【變色】和【旋轉】,【旋轉】又分【左旋】和【右旋】","attrs":{}},{"type":"text","text":",兩種方式可相互結合。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面我們從插入和刪除兩種場景來舉例說明。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"紅黑樹節點插入:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當我們插入值爲 66 的節點時,紅黑樹變成了這樣:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/44/44c28950f0bf4ea85712c2edcbbaa917.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"很明顯,這個時候結構依然遵循着上述 6 大規則,無需啓動自動平衡機制調整節點平衡狀態。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果再向裏面插入值爲 51 的節點,這個時候紅黑樹變成了這樣:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/9b/9be71971ecdfbfd0c619e36bf48029a2.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"很明顯現在的結構不遵循規則 4 了,這個時候就需要啓動自動平衡機制調整節點平衡狀態。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"關於C/C++ Linux後端開發網絡底層原理知識學習提升 點擊 ","attrs":{}},{"type":"link","attrs":{"href":"https://ke.qq.com/course/417774?flowToken=1013189","title":null,"type":null},"content":[{"type":"text","text":"學習資料","attrs":{}}]},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":" 獲取,內容知識點包括Linux,Nginx,ZeroMQ,MySQL,Redis,線程池,MongoDB,ZK,Linux內核,CDN,P2P,epoll,Docker,TCP/IP,協程,DPDK等等。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a2/a279659463c84a48ea38f92d864eae90.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"變色:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們可以通過變色的方式,使結構滿足紅黑樹的規則:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先解決結構不遵循規則 4 這一點(紅色節點相連,節點 49-51),需將節點 49 改爲黑色。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此時我們發現又違反了規則 5(56-49-51-XX 路徑中黑色節點超過了其他路徑),那麼我們將節點 45 改爲紅色節點。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"哈哈,妹的,又違反了規則 4(紅色節點相連,節點 56-45-43),那麼我們將節點 56 和節點 43 改爲黑色節點。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是我們發現此時又違反了規則 5(60-56-XX 路徑的黑色節點比 60-68-XX 的黑色節點多),因此我們需要調整節點 68 爲黑色。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"完成!","attrs":{}}]}]}],"attrs":{}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/00/007faee8f95b3ecb2b93bed34aa156da.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最終調整完成後的樹爲:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/4c/4c7a30f1ddcb7bfcd2303d7c9b06b6c4.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"但並不是什麼時候都那麼幸運,可以直接通過變色就達成目的,大多數時候還需要通過旋轉來解決。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如在下面這棵樹的基礎上,加入節點 65:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/44/44c28950f0bf4ea85712c2edcbbaa917.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"插入節點 65 後進行以下步驟:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/cb/cb8b7e866360e8a2d3325bd77f1c4cc2.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個時候,你會發現對於節點 64 無論是紅色節點還是黑色節點,都會違反規則 5,路徑中的黑色節點始終無法達成一致,這個時候僅通過【變色】已經無法達成目的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們需要通過旋轉操作,當然【旋轉】操作一般還需要搭配【變色】操作。旋轉包括【左旋】和【右旋】。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"左旋:","attrs":{}},{"type":"text","text":"逆時針旋轉兩個節點,讓一個節點被其右子節點取代,而該節點成爲右子節點的左子節點。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"左旋操作步驟如下:","attrs":{}},{"type":"text","text":"首先斷開節點 PL 與右子節點 G 的關係,同時將其右子節點的引用指向節點 C2;然後斷開節點 G 與左子節點 C2 的關係,同時將 G 的左子節點的應用指向節點 PL。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/94/9466c3102beb3b72a51b8e17a0392b71.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"右旋:","attrs":{}},{"type":"text","text":"順時針旋轉兩個節點,讓一個節點被其左子節點取代,而該節點成爲左子節點的右子節點。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"右旋操作步驟如下:","attrs":{}},{"type":"text","text":"首先斷開節點 G 與左子節點 PL 的關係,同時將其左子節點的引用指向節點 C2;然後斷開節點 PL 與右子節點 C2 的關係,同時將 PL 的右子節點的應用指向節點 G。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/bb/bb92f1ed37672dc3e55375665aedd46e.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"無法通過變色而進行旋轉的場景分爲以下四種:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"第一種:左左節點旋轉","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這種情況下,父節點和插入的節點都是左節點,如下圖(旋轉原始圖1)這種情況下,我們要插入節點 65。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"規則如下:","attrs":{}},{"type":"text","text":"以祖父節點【右旋】,搭配【變色】。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e1/e113e2db4562a92d7521f28c174d9b90.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"按照規則,步驟如下:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a0/a0cebd00c1c7aafb52b5fe6859045158.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"第二種:左右節點旋轉","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這種情況下,父節點是左節點,插入的節點是右節點,在旋轉原始圖 1 中,我們要插入節點 67。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"規則如下:","attrs":{}},{"type":"text","text":"先父節點【左旋】,然後祖父節點【右旋】,搭配【變色】。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"按照規則,步驟如下:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/73/737f7dd24ae931bdc3ee754c7954034e.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"第三種:右左節點旋轉","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這種情況下,父節點是右節點,插入的節點是左節點,如下圖(旋轉原始圖 2)這種情況,我們要插入節點 68。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"規則如下:先父節點【右旋】,然後祖父節點【左旋】,搭配【變色】。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/fa/fa3742f811801a7a5948e66a1bec0c69.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"按照規則,步驟如下:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/8d/8dd8590b1badea36baff685c7b2fa1b1.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"第四種:右右節點旋轉","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這種情況下,父節點和插入的節點都是右節點,在旋轉原始圖 2 中,我們要插入節點 70。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"規則如下:以祖父節點【左旋】,搭配【變色】。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"按照規則,步驟如下:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/4d/4da572cb787cbdf41108b48230ebf21d.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"紅黑樹插入總結","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"紅黑樹插入總結如下圖:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d2/d22d8241c38823a6e0dcdaae8ce6aa3e.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"紅黑樹節點刪除:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"相比較於紅黑樹的節點插入,刪除節點更爲複雜,我們從子節點是否爲 null 和紅色爲思考維度來討論。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"子節點至少有一個爲 null","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當待刪除的節點的子節點至少有一個爲 null 節點時,刪除了該節點後,將其有值的節點取代當前節點即可。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"若都爲 null,則將當前節點設置爲 null,當然如果違反規則了,則按需調整,如【變色】以及【旋轉】。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/82/8228d55d38368a6f556dfbd851b1f0e1.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"子節點都是非 null 節點","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這種情況下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"第一步:找到該節點的前驅或者後繼。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"前驅:","attrs":{}},{"type":"text","text":"左子樹中值最大的節點(可得出其最多隻有一個非 null 子節點,可能都爲 null)。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"後繼:","attrs":{}},{"type":"text","text":"右子樹中值最小的節點(可得出其最多隻有一個非 null 子節點,可能都爲 null)。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前驅和後繼都是值最接近該節點值的節點,類似於該節點.prev=前驅,該節點.next=後繼。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"第二步:將前驅或者後繼的值複製到該節點中,然後刪掉前驅或者後繼。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果刪除的是左節點,則將前驅的值複製到該節點中,然後刪除前驅;如果刪除的是右節點,則將後繼的值複製到該節點中,然後刪除後繼。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這相當於是一種“取巧”的方法,我們刪除節點的目的是使該節點的值在紅黑樹上不存在。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因此專注於該目的,我們並不關注刪除節點時是否真是我們想刪除的那個節點,同時我們也不需考慮樹結構的變化,因爲樹的結構本身就會因爲自動平衡機制而經常進行調整。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前面我們已經說了,我們要刪除的實際上是前驅或者後繼,因此我們就以前驅爲主線來講解。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"後繼的學習可參考前驅,包括下面幾種情況:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"①前驅爲黑色節點,並且有一個非 null 子節點","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/dc/dc34ad67d8c9687eb7e936b7f0f72ab4.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"分析:","attrs":{}},{"type":"text","text":"因爲要刪除的是左節點 64,找到該節點的前驅 63;然後用前驅的值 63替換待刪除節點的值 64,此時兩個節點(待刪除節點和前驅)的值都爲 63;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"刪除前驅 63,此時成爲上圖過程中間環節,但我們發現其不符合紅黑樹規則 4,因此需要進行自動平衡調整。這裏直接通過【變色】即可完成。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"②前驅爲黑色節點,同時子節點都爲 null","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/64/64230d0413adc3a74b5e79222b84502b.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"分析:","attrs":{}},{"type":"text","text":"因爲要刪除的是左節點 64,找到該節點的前驅 63;然後用前驅的值 63 替換待刪除節點的值 64,此時兩個節點(待刪除節點和前驅)的值都爲 63。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"刪除前驅 63,此時成爲上圖過程中間環節,但我們發現其不符合紅黑樹規則 5,因此需要進行自動平衡調整。這裏直接通過【變色】即可完成。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"③前驅爲紅色節點,同時子節點都爲 null","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/5a/5af1940c92d55b43f0180fed72fe8f3f.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"分析:","attrs":{}},{"type":"text","text":"因爲要刪除的是左節點 64,找到該節點的前驅 63;然後用前驅的值 63替換待刪除節點的值 64,此時兩個節點(待刪除節點和前驅)的值都爲 63;刪除前驅 63,樹的結構並沒有打破規則。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"紅黑樹刪除總結","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"紅黑樹刪除的情況比較多,但也就存在以下情況:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"刪除的是根節點,則直接將根節點置爲 null。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"待刪除節點的左右子節點都爲 null,刪除時將該節點置爲 null。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"待刪除節點的左右子節點有一個有值,則用有值的節點替換該節點即可。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"待刪除節點的左右子節點都不爲 null,則找前驅或者後繼,將前驅或者後繼的值複製到該節點中,然後刪除前驅或者後繼。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"節點刪除後可能會造成紅黑樹的不平衡,這時我們需通過【變色】+【旋轉】的方式來調整,使之平衡,上面也給出了例子,建議大家多多練習,而不必背下來。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"總結","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文主要介紹了紅黑樹的相關原理,首先紅黑樹的基礎二叉搜索樹,我們先簡單說了一下二叉搜索樹,並且講了一下搜索的流程。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後就針對紅黑樹的六大規則特點,紅黑樹的插入操作,刪除操作,都使用了大量的圖形來加以說明。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"紅黑樹的使用非常廣泛,如 TreeMap 和 TreeSet 都是基於紅黑樹實現的,而 JDK8 中 HashMap 當鏈表長度大於 8 時也會轉化爲紅黑樹。","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章