JS 实现红黑树(Red Black Tree)
文章目录
- JS 实现红黑树(Red Black Tree)
- 前言
- 概念
- 树的定义
- 插入操作
- 疑问一:新增结点是红是黑
- 插入,简单记录
- 删除操作
- 删除结点
- 平衡结点
- 单边结点
- 叶子结点
- 结语
- 代码
JS 实现红黑树(Red Black Tree)
前言
多的不说,算法导论,Data Structure 动态演示网站推荐一波。如果想学会红黑树这种实现逻辑比较复杂,看书是必要的,书的知识体系比较健全,讲解也是循序渐进,当你尝试过自己实现红黑树,又看过其他讲解红黑树博客,再回过头来看书,很多疑问就迎刃而解了。
写博客啊,写博客啊,其实是不想写的,尤其是这种,先学完了,代码实现了,再回过头来做记录,我感觉我全身都在抗拒,但,还是憋出来一点总结吧,有始有终。
算法导论第三版练习题答案
Data Structure Visualizations
彻底理解红黑树(三)之 删除
红黑树详细分析,看了都说好
美团技术沙龙-红黑树
硬核图解面试最怕的红黑树【建议反复摩擦】
概念
红黑树(Red Black Tree) 是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。
红黑树是一种平衡二叉查找树的变体,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),但 对之进行平衡的代价较低, 其平均统计性能要强于 AVL 。
特征:
性质1. 结点是红色或黑色。 [3]
性质2. 根结点是黑色。 [3]
性质3. 所有叶子都是黑色。(叶子是NIL结点) [3]
性质4. 每个红色结点的两个子结点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色结点)
性质5. 从任一节结点其每个叶子的所有路径都包含相同数目的黑色结点。 [3]
1.严格满足二叉排序树的条件
2.不严格满足平衡二叉树(AVL)条件,左右子树高度差有可能大于 1。
注意: AVL树以平衡因子为平衡条件,红黑树以其五点性质为平衡条件,虽都有保持平衡的共性,但实际上红黑树中不用左右树高(height)做平衡,所以高度差可能会大于1。
树的定义
key
,color
,left
,right
,是红黑树必须定义的四个属性。其他属性如父指针(parent
),树高(height
),黑高(blackHeight
),按需定义,最一开始,我没有定义parent
,后来老老实实把他加上了,为什么?因为确实好用。
let TreeNode = function (value) {this.key = valuethis.color = truethis.parent = nullthis.left = nullthis.right = null}
插入操作
疑问一:新增结点是红是黑
因为一开始没有看资料,是直接从概念上手的,所以这是我思考的第一个问题,新增结点是红是黑,好像都可以是吧,定义上又没有规定,当然,人总是要选容易走的路,当时是“直觉”是让我选了红色。(新增节点为黑色,让我感觉情况很复杂)
后面看了书,书上有个思考题:算法导论第三版练习题答案
插入结点为红色,可以避免影响性质五(黑高),emm,大概是这意思。
插入,简单记录
当新增节点定为红色后,插入的情况其实很简单,可能会破坏的性质就是四(双红,我是这么取别名的),情况其实有五种,但有两种是RR和RL的双红,与LL,LR是镜像的,就省略了,如下:
情况简单,平衡操作,我稍作记录如下,
1、红黑置换平衡红黑树(能通过置换平衡的,优先置换)
情况三,出现了两个连续的红结点,因为U结点也是红色,可以先通过置换颜色达到平衡,将GP结点设为新平衡结点,再判断是否出现了连续的红色结点。
while (isRed(node.parent)) {//....省略let uncle = node.parent.parent.rightif (isRed(uncle)) {node.parent.color = falseuncle.color = falseuncle.parent.color = truenode = node.parent.parentcontinue}
//....省略
}
2、旋转平衡红黑树
删除操作
我看的资料里,删除的方法有两种:
1、硬核图解面试最怕的红黑树【建议反复摩擦】,沿查询路径,一路调整红黑树,确保删除结点为红色,直接删除,随后沿原路径修复红黑树。
2、直接删除结点,再根据情况对红黑树进行平衡操作。
我这里,研究的是第二种,主要参考的算法导论,使用了NIL哨兵结点,父指针来简化判断(我一开始,没用哨兵,没用父指针,怼了三天三夜,人炸了,写的删除代码逻辑还没有闭环,部分情况下,未达到平衡,然后痛定思痛,下载了算法导论)
删除结点
这一步,容易,跟二叉搜索树的逻辑一样的,只不过多了NIL,删除操作就变成了调整其父子结点的指针。定义了一个 originDeleteColor
局部变量,记录删除结点的颜色,用以进行是否需要进行平衡操作的判断。
if (!originDeleteColor) {//删除节点为黑色fixUpDelete(balanceNode)}
平衡结点
单边结点
单边结点平衡简单,就一种情况,将红儿子置黑就行,这里暂时忽略
叶子结点
上文说到,我怼了三天三夜,逻辑没有写闭环。原因就是我关注的重点不对,我从删除结点出发,穷举式的推演出了所有情况,对情况一一做了处理,但这种方式搞出的状况,太多了,判断不过来,也无法对类似状况进行归纳。(我以为把情况全部画出来,就能找到规律,但并没有)
而现在,我有了新的思路,重点关注红色结点,而不是直接推演所有情况。
1、我们需要一个红色结点
已知,删除节点是黑色的,如下图,我们想维护平衡,就只能为左边"增加"一个黑色结点,同时不影响右边黑高,也就是说,我们需要一个红色结点。
2、哪里的红色结点能帮到我们
其实,只要是在查询删除路径下不超过两次旋转操作的红色结点就能帮到我们(如下图红圈范围内,没红结点的时候,我们只能沿路径往上找),但我们不能先拿远水来救近火,所以,优先利用红圈内红色结点。
如此转变,我们思考的问题就少了很多,不用先去分析各种状况,只需要考虑红色结点位置就行了。
疑问:为什么不优先利用出现在P的红色结点?明明 P 最近
造孽,我之前就是先从 P 结点开始分析的,优先级搞错,困难加倍。
不选P的原因其实很简单,可能会破坏其他的性质(性质4),即我们调整后仍需要对BL,BR做判断,再决定如何修复性质四。
这跟BL,BR的选择问题类似,如下图(情况四),BR优先级肯定比BL高。实践上,我们可以对红色结点按优先级进行排名,即 B > BR > BL > P (BR,BL 顺序以实际情况为准,本文所列情况,BR 优先)
这样思路一下清晰了。如下图,这看上去就很有规律。
注意:P结点颜色不变,指的是原P结点位置被旋转结点替换后颜色根原P结点颜色一致。
情况一:平衡操作记录
平衡结点(D),因旋转获得了新兄弟结点(BL)转成情况二或三或四
为什么一次旋转后仍未平衡?
如下图所示,因平衡结点默认黑高少一,而B结点为红色,所以D为根结点的树(后面简称D树)比BL为根结点的树(简称BL树)黑高少一,旋转后,D树和BL树成了兄弟,黑高应一致,所以仍需平衡。
ps:情况三或情况四,因兄弟结点为黑色,旋转不会产生此问题。
情况三、情况四:平衡操作记录,(此处忽略了旋转操作中,部分可能存在的孩子结点指针变化操作)
情况二:平衡操作记录。
1、 P结点为红或为根节点,P置黑,B置红,修复黑高,平衡结束
2、P为黑,非根,B置红,以P为新的平衡结点,重新进入循环。
function fixUpDelete(balanceNode) {let brotherwhile (balanceNode !== root && !isRed(balanceNode)) {if (balanceNode === balanceNode.parent.left) {brother = balanceNode.parent.rightif (isRed(brother)) {// 情况一brother.color = falsebalanceNode.parent = trueleftRotate(balanceNode.parent)brother = balanceNode.parent.right}if (!isRed(brother.left) && !isRed(brother.right)) {// 情况二brother.color = truebalanceNode = balanceNode.parentcontinue} else if (!isRed(brother.right)) {// 情况三brother.left.color = falsebrother.color = truerightRotate(brother)brother = balanceNode.parent.right}// 情况四brother.color = balanceNode.parent.colorbalanceNode.parent.color = falsebrother.right.color = falseleftRotate(balanceNode.parent)balanceNode = root} else {// 对称操作}}balanceNode.color = false}
结语
终终终于总结完了,本来想简单糊弄一下的,但总结过程中,又不断的发现了自己之前没注意到的问题,然后不断补充,使文章记录的内容变多了不少。
代码
码云代码仓
JS 实现红黑树(Red Black Tree)相关推荐
- 红黑树Red/Black Tree
红黑树Red/Black Tree 建立二进制搜索树,我们得到红/黑树,旨在解决BST可能变得不平衡的问题.(BST[二叉搜索树],是对于任意的node x,如果node y是node x的左边的节点 ...
- 数据结构--红黑树 Red Black Tree
文章目录 1.概念 2.操作 2.1 左旋.右旋(围绕某个节点的左/右旋) 2.2 插入 2.3 删除 3. 代码 1.概念 二叉树在频繁动态增删后,可能退化成链表,时间复杂度由 O(lgn) 变成 ...
- 红黑树(Red Black Tree)详解
红黑树 红黑树(Red Black Tree) 是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组. 红黑树是在1972年由Rudolf Bayer发明的,当时被称为 ...
- 红黑树(Red Black Tree)超详细解析
红黑树详解 什么是红黑树? 红黑树,是一种二叉搜索树的特化,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black. 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确 ...
- 红黑树(Red–black tree)
一.红黑树的概念: 在计算机科学中,红黑树是一种自平衡二叉搜索树.每个节点存储一个表示"颜色"("红"或"黑")的额外位,用于确保树在插入和 ...
- 数据结构——红黑树(red-black tree)
RB-tree(红黑树)是一种平衡二叉搜索树,它每个节点上增加了一个存储位来表示节点的颜色,可以是 Red 或 Black,故得名.通过对任何一条从根到叶子的简单路径上各个节点的颜色进行约束,红黑树能 ...
- 红黑树(Red-Black Tree)解析
这一篇我们来聊聊红黑树,写这篇文章的起因是在阅读HashMap源码时,发现JDK1.8对于HashMap的实现引入了红黑树来处理哈希冲突以提高性能(戳这里,有详述),而红黑树的数据结构和操作都是较为复 ...
- 红黑树(Red-Black Tree,RBT)
1.红黑树 强烈推荐!!算法交互式网站: Red/Black Tree 在insert左侧框输入要插入的数字,然后点击insert就会按红黑树规则进行插入 1.1 什么是红黑树? 红黑树是一种平衡二叉 ...
- 高级数据结构与算法 | 红黑树(Red-Black Tree)
文章目录 红黑树 红黑树的概念 红黑树的性质 红黑树与AVL树 红黑树的实现 红黑树的节点 红黑树的插入 红黑树的查找 红黑树的验证 完整代码 红黑树 红黑树的概念 红黑树,是一种二叉搜索树,但在每个 ...
最新文章
- 笔记 JVM调优流程
- SolidWorks转二维平面图
- 程序猿必备的10款web前端开发插件一
- 位居新品第一、单品第二,乐视1s吊打了谁的耳光?
- ant更改主题色报错Inline JavaScript is not enabled. Is it set in your options? vue ant主题色更改 vue-cli3
- JBoss5开发web service常见问题
- Carlosfu技术系列文章总目录
- 5.1 百度寻人 ios解析 和 天气预报解析
- 单片机涡轮流量传感器_青天仪表为您介绍安装涡轮流量计需要注意问题
- 人人商城删除后台菜单“小程序”
- 管理,就是做减法!聊聊 “奥卡姆剃刀定律”
- 对称加密/非对称加密
- linux 创建子进程,Linux中使用fork创建子进程详解及示例程序
- 【STM32】PWM 输出 (标准库)
- 集古今异宝 供八方收藏
- FullCalendar demo实例
- 金山打字通2008完整版包含金山打字游戏,网上唯一的
- 微信小程序----折叠面板(MUI折叠面板)
- Springboot优雅的参数校验(一)
- 计算机考试flash格式是什么,WPS演示绝对快速实现Flash插入