摘要:算法導論由于性質,紅黑樹中不會出現(xiàn)兩個紅色結點相鄰的情形。紅黑樹的插入首先以二叉搜索樹的方式插入結點,并將其著為紅色。假設整棵二叉搜索樹中,只有和因違背性質而無法成為正常的紅黑樹,此時將圖調整成圖,則可以恢復成正常的紅黑樹。
紅黑樹的性質
一棵滿足以下性質的二叉搜索樹是一棵紅黑樹
每個結點或是黑色或是紅色。
根結點是黑色的。
每個葉結點(NIL)是黑色的。
如果一個結點是紅色的,則它的兩個子結點都是黑色的。
對每個結點,從該結點到其所有后代葉結點的簡單路徑上,均包含相同數(shù)目的黑色結點。
性質1和性質2,不用做過多解釋。
性質3,每個葉結點(NIL)是黑色的。這里的葉結點并不是指上圖中結點1,5,8,15,而是指下圖中值為null的結點,它們的顏色為黑色,且是它們父結點的子結點。
性質4,如果一個結點是紅色的(圖中用白色代替紅色),則它的兩個子結點都是黑色的,例如結點2,5,8,15。但是,如果某結點的兩個子結點都是黑色的,該結點未必是紅色的,例如結點1。
性質5,對每個結點,從該結點到其所有后代葉結點的簡單路徑上,均包含相同數(shù)目的黑色結點。例如,從結點2到其所有后代葉結點的簡單路徑上,黑色結點的數(shù)量都為2;從根結點11到其所有后代葉結點的簡單路徑上,黑色結點的數(shù)量都為3。
這樣的一棵樹有什么特點呢?
通過對任何一條從根到葉結點的簡單路徑上各個結點的顏色進行約束,紅黑樹確保沒有一條路徑會比其他路徑長出2倍,因為是近似于平衡的。——《算法導論》
由于性質4,紅黑樹中不會出現(xiàn)兩個紅色結點相鄰的情形。樹中最短的可能出現(xiàn)的路徑是都是黑色結點的路徑,樹中最長的可能出現(xiàn)的路徑是紅色結點和黑色結點交替的路徑。再結合性質5,每條路徑上均包含相同數(shù)目的黑色結點,所以紅黑樹確保沒有一條路徑會比其他路徑長出2倍。
紅黑樹的插入首先以二叉搜索樹的方式插入結點,并將其著為紅色。如果著為黑色,則會違背性質5,不便調整;如果著為紅色,可能會違背性質2或性質4,可以通過相對簡單的操作,使其恢復紅黑樹的性質。
一個結點以二叉搜索樹的方式被插入后,可能出現(xiàn)以下幾種情況:
情形1插入結點后,無父結點,結點插入成為根結點,違背性質2,將結點調整為黑色,完成插入。
情形2插入結點后,其父結點為黑色,沒有違背任何性質,不用調整,完成插入。例如下圖中插入結點13。
情形3插入結點后,其父結點為紅色,違背了性質4,需要采取一系列的調整。例如下圖中插入結點4。
那么一系列的調整是什么呢?
如果插入結點node的父結點father為紅色,則結點father必然存在黑色的父結點grandfather,因為如果結點father不存在父結點的話,就是根結點,而根結點是黑色的。那么結點grandfather的另一個子結點,我們可以稱之為結點uncle,即結點father的兄弟結點。結點uncle可能為黑色,也可能為紅色。
先從最簡單的情形分析,因為復雜的情形可以轉化為簡單的情形,簡單的情形就是結點uncle為黑色的情形。
情形3.1如上圖(a)中,情形是這樣的,node 為紅,father 為紅,grandfather 和 uncle 為黑,α,β,θ,ω,η 都是結點對應的子樹。假設整棵二叉搜索樹中,只有node和father因違背性質4而無法成為正常的紅黑樹,此時將圖(a)調整成圖(b),則可以恢復成正常的紅黑樹。整個調整過程中實際分為兩步,旋轉和變色。
什么是旋轉?
如上圖(c)是一棵二叉搜索樹的一部分,其中 x, y 是結點,α,β,θ 是對應結點的子樹。由圖可知,α < x < β < y < θ ,即 α子樹中的所有結點都小于x,結點 x都小于 β子樹中的所有結點,β子樹中的所有結點的值都小于結點 y 的值,結點 y 的值都小于 θ子樹中的所有結點。在二叉搜索樹中,如果結點y的值比結點x的值大,那么結點x在結點y的左子樹中,如圖(c);或者結點y在結點x的右子樹中,如圖(d)。故 α < x < β < y < θ ,也可以用圖(d)的結構來表現(xiàn)。這就是旋轉,它不會破壞二叉搜索樹的性質。
node 為紅,father 為紅,grandfather 和 uncle 為黑的具體情形一
圖(a)中,node 為 father 的左子結點, father 為 grand 的左子結點,node < father < θ < grand < uncle。這種情形中 father < grand,即可以表現(xiàn)為 father 是 grand 的左子樹,也可以表現(xiàn)為 grand 是 father 的右子樹,故將圖(a)中 father 和 grand 旋轉,旋轉雖然不會破壞二叉搜索樹的性質,但是旋轉之后,會破壞紅黑樹的性質,所以還需要調整結點的顏色。
變色
所以圖(a)旋轉過后,還要將 grand 變?yōu)榧t色,father 變?yōu)楹谏?,變成圖(b),完成插入。
node 為紅,father 為紅,grandfather 和 uncle 為黑的具體情形二
node 為 father 的右子結點, father 為 grand 的右子結點,如下圖(e),就是具體情形一的翻轉。
即,uncle < grand < θ < father < node ,將圖(e)中 father 和 grand 旋轉,變色后,變成圖(f),完成插入。
node 為紅,father 為紅,grandfather 和 uncle 為黑的具體情形三
node 為 father 的右子結點, father 為 grand 的左子結點,如下圖(m)。
將圖(m)中 node 和 father 旋轉后,變成圖(n),將father看作新的node,就成為了具體情形一,再次旋轉,變色后,完成插入。
node 為紅,father 為紅,grandfather 和 uncle 為黑的具體情形四
node 為 father 的右子結點, father 為 grand 的左子結點,如下圖(i),就是具體情形三的翻轉。
將圖(i)中 node 和 father 旋轉后,變成圖(j),將father看作新的node,就成為了具體情形二,再次旋轉,變色后,完成插入。
情形3.2node ,father 和 uncle 為紅,grandfather 為黑。
如上圖(k),不旋轉,而是將grand著紅,father和uncle著黑,同時將grand作為新的node,進行情形的判斷。如果grand作為新的node后,變成了情形2,則插入完成;如果變成了情形3.1,則調整后,插入完成;如果仍是情形3.2,則繼續(xù)將grand,father和uncle變色,和node結點上移,如果新的node結點沒有父節(jié)點了,則變成了情形1,將根結點著為黑色,那么插入完成。
綜上node的情形 | 操作 | |
---|---|---|
情形1 | node為紅,無father | 將node重新著色 |
情形2 | node為紅,father為黑 | |
情形3.1 | node,father為紅,grand,uncle為黑 | 旋轉一次或兩次,并重新著色 |
情形3.2 | node,father,uncle為紅,grand為黑 | 將father, uncle,grand重新著色, grand作為新的node |
// 結點 function Node(value) { this.value = value this.color = "red" // 結點的顏色默認為紅色 this.parent = null this.left = null this.right = null } function RedBlackTree() { this.root = null } RedBlackTree.prototype.insert = function (node) { // 以二叉搜索樹的方式插入結點 // 如果根結點不存在,則結點作為根結點 // 如果結點的值小于node,且結點的右子結點不存在,跳出循環(huán) // 如果結點的值大于等于node,且結點的左子結點不存在,跳出循環(huán) if (!this.root) { this.root = node } else { let current = this.root while (current[node.value <= current.value ? "left" : "right"]) { current = current[node.value <= current.value ? "left" : "right"] } current[node.value <= current.value ? "left" : "right"] = node node.parent = current } // 判斷情形 this._fixTree(node) return this } RedBlackTree.prototype._fixTree = function (node) { // 當node.parent不存在時,即為情形1,跳出循環(huán) // 當node.parent.color === "black"時,即為情形2,跳出循環(huán) while (node.parent && node.parent.color !== "black") { // 情形3 let father = node.parent let grand = father.parent let uncle = grand[grand.left === father ? "right" : "left"] if (!uncle || uncle.color === "black") { // 葉結點也是黑色的 // 情形3.1 let directionFromFatherToNode = father.left === node ? "left" : "right" let directionFromGrandToFather = grand.left === father ? "left" : "right" if (directionFromFatherToNode === directionFromGrandToFather) { // 具體情形一或二 // 旋轉 this._rotate(father) // 變色 father.color = "black" grand.color = "red" } else { // 具體情形三或四 // 旋轉 this._rotate(node) this._rotate(node) // 變色 node.color = "black" grand.color = "red" } break // 完成插入,跳出循環(huán) } else { // 情形3.2 // 變色 grand.color = "red" father.color = "black" uncle.color = "black" // 將grand設為新的node node = grand } } if (!node.parent) { // 如果是情形1 node.color = "black" this.root = node } } RedBlackTree.prototype._rotate = function (node) { // 旋轉 node 和 node.parent let y = node.parent if (y.right === node) { if (y.parent) { y.parent[y.parent.left === y ? "left" : "right"] = node } node.parent = y.parent if (node.left) { node.left.parent = y } y.right = node.left node.left = y y.parent = node } else { if (y.parent) { y.parent[y.parent.left === y ? "left" : "right"] = node } node.parent = y.parent if (node.right) { node.right.parent = y } y.left = node.right node.right = y y.parent = node } } let arr = [11, 2, 14, 1, 7, 15, 5, 8, 4, 16] let tree = new RedBlackTree() arr.forEach(i => tree.insert(new Node(i))) debugger
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://m.hztianpu.com/yun/89928.html
摘要:很多文章或書籍在介紹紅黑樹的時候直接上來就是紅黑樹的個基本性質插入刪除操作等。這也不奇怪,算法的作者就是紅黑樹的作者之一。所以,理解樹對掌握紅黑樹是至關重要的。 本文主要包括以下內容: 什么是2-3樹 2-3樹的插入操作 紅黑樹與2-3樹的等價關系 《算法4》和《算法導論》上關于紅黑樹的差異 紅黑樹的5條基本性質的分析 紅黑樹與2-3-4樹的等價關系 紅黑樹的插入、刪除操作 JDK ...
摘要:用這種范例表示紅黑樹是可能的,但是這會改變一些性質并使算法復雜。插入會出現(xiàn)種情況為根節(jié)點,即紅黑樹的根節(jié)點。 說到HashMap,就一定要說到紅黑樹,紅黑樹作為一種自平衡二叉查找樹,是一種用途較廣的數(shù)據(jù)結構,在jdk1.8中使用紅黑樹提升HashMap的性能,今天就來說一說紅黑樹。 前言 限于篇幅,本文只對紅黑樹的基礎進行說明,暫不涉及源碼部分,大部分摘抄自維基百科,這里也貼出對應鏈接...
摘要:需要執(zhí)行的操作依次是首先,將紅黑樹當作一顆二叉查找樹,將該節(jié)點從二叉查找樹中刪除然后,通過旋轉和重新著色等一系列來修正該樹,使之重新成為一棵紅黑樹。 雖是讀書筆記,但是如轉載請注明出處 http://segmentfault.com/blog/exploring/ .. 拒絕伸手復制黨 關于二叉樹的基本知識,可以參見:Java 實現(xiàn)基本數(shù)據(jù)結構 2(樹) 以下是算法導論第13章的學...
摘要:在上一節(jié)中,在中用了鏈表和紅黑樹兩種方式解決沖突,在中也是用紅黑樹存儲的。其中節(jié)點顏色為黑色紅黑樹的左旋和右旋紅黑樹的插入和刪除,都有可能破壞其特性,就不是一棵紅黑樹了,所以要調整。 在上一節(jié)中,HashMap在jdk 1.8中用了鏈表和紅黑樹兩種方式解決沖突,在TreeMap中也是用紅黑樹存儲的。下面分析一下紅黑樹的結構和基本操作。 一、紅黑樹的特征和基本操作 上一節(jié)中已經(jīng)描述了紅黑...
閱讀 3276·2021-11-10 11:35
閱讀 1477·2019-08-30 13:20
閱讀 1174·2019-08-29 16:18
閱讀 2207·2019-08-26 13:54
閱讀 2215·2019-08-26 13:50
閱讀 1009·2019-08-26 13:39
閱讀 2556·2019-08-26 12:08
閱讀 2008·2019-08-26 10:37