国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

紅黑樹的插入詳解及Javascript實現(xiàn)方法示例

2019-11-19 14:06:21
字體:
供稿:網(wǎng)友

紅黑樹的性質(zhì)

一棵滿足以下性質(zhì)的二叉搜索樹是一棵紅黑樹

  1. 每個結(jié)點或是黑色或是紅色。
  2. 根結(jié)點是黑色的。
  3. 每個葉結(jié)點(NIL)是黑色的。
  4. 如果一個結(jié)點是紅色的,則它的兩個子結(jié)點都是黑色的。
  5. 對每個結(jié)點,從該結(jié)點到其所有后代葉結(jié)點的簡單路徑上,均包含相同數(shù)目的黑色結(jié)點。

性質(zhì)1和性質(zhì)2,不用做過多解釋。

性質(zhì)3,每個葉結(jié)點(NIL)是黑色的。這里的葉結(jié)點并不是指上圖中結(jié)點1,5,8,15,而是指下圖中值為null的結(jié)點,它們的顏色為黑色,且是它們父結(jié)點的子結(jié)點。

性質(zhì)4,如果一個結(jié)點是紅色的(圖中用白色代替紅色),則它的兩個子結(jié)點都是黑色的,例如結(jié)點2,5,8,15。但是,如果某結(jié)點的兩個子結(jié)點都是黑色的,該結(jié)點未必是紅色的,例如結(jié)點1。

性質(zhì)5,對每個結(jié)點,從該結(jié)點到其所有后代葉結(jié)點的簡單路徑上,均包含相同數(shù)目的黑色結(jié)點。例如,從結(jié)點2到其所有后代葉結(jié)點的簡單路徑上,黑色結(jié)點的數(shù)量都為2;從根結(jié)點11到其所有后代葉結(jié)點的簡單路徑上,黑色結(jié)點的數(shù)量都為3。

這樣的一棵樹有什么特點呢?

通過對任何一條從根到葉結(jié)點的簡單路徑上各個結(jié)點的顏色進行約束,紅黑樹確保沒有一條路徑會比其他路徑長出2倍,因為是近似于平衡的。――《算法導(dǎo)論》

由于性質(zhì)4,紅黑樹中不會出現(xiàn)兩個紅色結(jié)點相鄰的情形。樹中最短的可能出現(xiàn)的路徑是都是黑色結(jié)點的路徑,樹中最長的可能出現(xiàn)的路徑是紅色結(jié)點和黑色結(jié)點交替的路徑。再結(jié)合性質(zhì)5,每條路徑上均包含相同數(shù)目的黑色結(jié)點,所以紅黑樹確保沒有一條路徑會比其他路徑長出2倍。

紅黑樹的插入

首先以二叉搜索樹的方式插入結(jié)點,并將其著為紅色。如果著為黑色,則會違背性質(zhì)5,不便調(diào)整;如果著為紅色,可能會違背性質(zhì)2或性質(zhì)4,可以通過相對簡單的操作,使其恢復(fù)紅黑樹的性質(zhì)。

一個結(jié)點以二叉搜索樹的方式被插入后,可能出現(xiàn)以下幾種情況:

情形1

插入結(jié)點后,無父結(jié)點,結(jié)點插入成為根結(jié)點,違背性質(zhì)2,將結(jié)點調(diào)整為黑色,完成插入。

情形2

插入結(jié)點后,其父結(jié)點為黑色,沒有違背任何性質(zhì),不用調(diào)整,完成插入。例如下圖中插入結(jié)點13。

情形3

插入結(jié)點后,其父結(jié)點為紅色,違背了性質(zhì)4,需要采取一系列的調(diào)整。例如下圖中插入結(jié)點4。

那么一系列的調(diào)整是什么呢?

如果插入結(jié)點node的父結(jié)點father為紅色,則結(jié)點father必然存在黑色的父結(jié)點grandfather,因為如果結(jié)點father不存在父結(jié)點的話,就是根結(jié)點,而根結(jié)點是黑色的。那么結(jié)點grandfather的另一個子結(jié)點,我們可以稱之為結(jié)點uncle,即結(jié)點father的兄弟結(jié)點。結(jié)點uncle可能為黑色,也可能為紅色。

先從最簡單的情形分析,因為復(fù)雜的情形可以轉(zhuǎn)化為簡單的情形,簡單的情形就是結(jié)點uncle為黑色的情形。

情形3.1

如上圖(a)中,情形是這樣的,node 為紅,father 為紅,grandfather 和 uncle 為黑,α,β,θ,ω,η 都是結(jié)點對應(yīng)的子樹。假設(shè)整棵二叉搜索樹中,只有node和father因違背性質(zhì)4而無法成為正常的紅黑樹,此時將圖(a)調(diào)整成圖(b),則可以恢復(fù)成正常的紅黑樹。整個調(diào)整過程中實際分為兩步,旋轉(zhuǎn)和變色。

什么是旋轉(zhuǎn)?

如上圖(c)是一棵二叉搜索樹的一部分,其中 x, y 是結(jié)點,α,β,θ 是對應(yīng)結(jié)點的子樹。由圖可知,α < x < β < y < θ ,即 α子樹中的所有結(jié)點都小于x,結(jié)點 x都小于 β子樹中的所有結(jié)點,β子樹中的所有結(jié)點的值都小于結(jié)點 y 的值,結(jié)點 y 的值都小于 θ子樹中的所有結(jié)點。在二叉搜索樹中,如果結(jié)點y的值比結(jié)點x的值大,那么結(jié)點x在結(jié)點y的左子樹中,如圖(c);或者結(jié)點y在結(jié)點x的右子樹中,如圖(d)。故 α < x < β < y < θ ,也可以用圖(d)的結(jié)構(gòu)來表現(xiàn)。這就是旋轉(zhuǎn),它不會破壞二叉搜索樹的性質(zhì)。

node 為紅,father 為紅,grandfather 和 uncle 為黑的具體情形一

圖(a)中,node 為 father 的左子結(jié)點, father 為 grand 的左子結(jié)點,node < father < θ < grand < uncle。這種情形中 father < grand,即可以表現(xiàn)為 father 是 grand 的左子樹,也可以表現(xiàn)為 grand 是 father 的右子樹,故將圖(a)中 father 和 grand 旋轉(zhuǎn),旋轉(zhuǎn)雖然不會破壞二叉搜索樹的性質(zhì),但是旋轉(zhuǎn)之后,會破壞紅黑樹的性質(zhì),所以還需要調(diào)整結(jié)點的顏色。

變色

所以圖(a)旋轉(zhuǎn)過后,還要將 grand 變?yōu)榧t色,father 變?yōu)楹谏兂蓤D(b),完成插入。

node 為紅,father 為紅,grandfather 和 uncle 為黑的具體情形二

node 為 father 的右子結(jié)點, father 為 grand 的右子結(jié)點,如下圖(e),就是具體情形一的翻轉(zhuǎn)。

即,uncle < grand < θ < father < node ,將圖(e)中 father 和 grand 旋轉(zhuǎn),變色后,變成圖(f),完成插入。

node 為紅,father 為紅,grandfather 和 uncle 為黑的具體情形三

node 為 father 的右子結(jié)點, father 為 grand 的左子結(jié)點,如下圖(m)。

將圖(m)中 node 和 father 旋轉(zhuǎn)后,變成圖(n),將father看作新的node,就成為了具體情形一,再次旋轉(zhuǎn),變色后,完成插入。

node 為紅,father 為紅,grandfather 和 uncle 為黑的具體情形四

node 為 father 的右子結(jié)點, father 為 grand 的左子結(jié)點,如下圖(i),就是具體情形三的翻轉(zhuǎn)。

將圖(i)中 node 和 father 旋轉(zhuǎn)后,變成圖(j),將father看作新的node,就成為了具體情形二,再次旋轉(zhuǎn),變色后,完成插入。

情形3.2

node ,father 和 uncle 為紅,grandfather 為黑。

如上圖(k),不旋轉(zhuǎn),而是將grand著紅,father和uncle著黑,同時將grand作為新的node,進行情形的判斷。如果grand作為新的node后,變成了情形2,則插入完成;如果變成了情形3.1,則調(diào)整后,插入完成;如果仍是情形3.2,則繼續(xù)將grand,father和uncle變色,和node結(jié)點上移,如果新的node結(jié)點沒有父節(jié)點了,則變成了情形1,將根結(jié)點著為黑色,那么插入完成。

綜上

node的情形 操作
情形1 node為紅,無father 將node重新著色
情形2 node為紅,father為黑
情形3.1 node,father為紅,grand,uncle為黑 旋轉(zhuǎn)一次或兩次,并重新著色
情形3.2 node,father,uncle為紅,grand為黑 將father, uncle,grand重新著色, grand作為新的node

代碼

// 結(jié)點function Node(value) { this.value = value this.color = 'red' // 結(jié)點的顏色默認為紅色 this.parent = null this.left = null this.right = null}function RedBlackTree() { this.root = null}RedBlackTree.prototype.insert = function (node) { // 以二叉搜索樹的方式插入結(jié)點 // 如果根結(jié)點不存在,則結(jié)點作為根結(jié)點 // 如果結(jié)點的值小于node,且結(jié)點的右子結(jié)點不存在,跳出循環(huán) // 如果結(jié)點的值大于等于node,且結(jié)點的左子結(jié)點不存在,跳出循環(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') {  // 葉結(jié)點也是黑色的  // 情形3.1  let directionFromFatherToNode = father.left === node ? 'left' : 'right'  let directionFromGrandToFather = grand.left === father ? 'left' : 'right'  if (directionFromFatherToNode === directionFromGrandToFather) {  // 具體情形一或二  // 旋轉(zhuǎn)  this._rotate(father)  // 變色  father.color = 'black'  grand.color = 'red'  } else {  // 具體情形三或四  // 旋轉(zhuǎn)  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設(shè)為新的node  node = grand } } if (!node.parent) { // 如果是情形1 node.color = 'black' this.root = node }}RedBlackTree.prototype._rotate = function (node) { // 旋轉(zhuǎn) 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

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對武林網(wǎng)的支持。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 乌拉特后旗| 页游| 和顺县| 社旗县| 山阴县| 漾濞| 喜德县| 南雄市| 子长县| 双牌县| 文水县| 株洲市| 偏关县| 三门峡市| 乐亭县| 常德市| 鄱阳县| 禹城市| 高密市| 临海市| 双江| 靖远县| 嘉禾县| 沾化县| 高清| 张家口市| 焉耆| 宁陵县| 屏东县| 贺州市| 托里县| 大安市| 垦利县| 年辖:市辖区| 江油市| 雷州市| 隆德县| 沐川县| 青田县| 大渡口区| 武穴市|