红黑树的删除详解与思路分析——不同于教科书上的算法(dart语言实现)
对于红黑树的删除,看了数据结构的书,也看了很多网上的讲解和实现,但都不满意。很多讲解都是囫囵吞枣,知其然,不知其所以然,讲的晦涩难懂。
红黑树是平衡二叉树的一种,其删除算法是比较复杂的,因为删除后还要保持红黑树的特性。红黑树的特性如下:
- 节点是红色或黑色。
- 根是黑色。
- 所有叶子都是黑色(叶子是NIL节点)。
- 每个红色节点必须有两个黑色的子节点。(从每个叶子到根的所有路径上不能有两个连续的红色节点。)
- 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点(简称黑高)。
因此,从红黑树最基础的特性出发,抛开教科书和网上的算法,画了无数张图,分析了多种可能的情况以后,经过归纳提炼,实现了不同于教科书上的删除算法。
经过多次画图证明以后,笔者发现,红黑树的删除算法不是唯一的,不管如何调整,只要保证删除后还是一颗红黑树即可。
因此,笔者实现的 删除思路和算法如下:
1. 删除转移:(这部分是大路货,不是自己实现的)
- 如果被删除节点有两个非空子节点,则用后继节点的值代替该节点的值,这样演变成了删除后继节点;否则转下一条;
- 如果被删除节点一个或两个孩子都为空:若有非空孩子,则用非空孩子节点替代之;若无,直接删除;
- 删除后继节点:后继节点的左孩子节点一定为空,右孩子可能为空;处理如上一条;
删除转移的目的是为了简化删除操作,更是为了简化修复操作。因为删除转移后,最终待删除的节点最多只会有一个非空孩子。
2. 删除后修复:
2.1 简单的情况:
- 若被删除节点为红色节点,不需修复;此时该节点一定为红色的叶子节点(可根据红黑树的特性证明);
- 若被删除的节点为黑色节点,且有一个非空子节点,则将其非空子节点颜色涂黑即可;
对于以上两种简单的情况,做个说明:根据红黑树特性,非空子节点一定为红色节点,否则将违反特性;根据红黑树特性,在删除前,一颗红黑树不可能出现以下几种情况:
(图片来自网络,感谢原作者。)
2.2 复杂的情况:删除后需要修复的。
只有当被删除的节点为黑色叶子节点时,导致该节点所在的分支,少了一个黑色节点,树不再平衡,因此需要修复。
修复的整体思路是:
- 如果该节点的父节点、或兄弟节点、或兄弟节点的特定方向的子节点 中,有红色节点,则将此红色节点旋转过来,通过旋转、涂黑操作,保持自父节点以来的树的平衡;
- 如果不满足上述条件,则通过旋转和变色操作,使其兄弟分支上也减少一个黑色节点,这样自父节点以来的分支保持了平衡,满足了条件,但对于父节点来说,其整个分支减少了一个黑色节点,需要递归向上处理,直至重新平衡,或者到达根节点;
掌握了整体思路以后,就可编码实现了,编码中用了一些小技巧,合并了一些情况,代码比较简单易懂,阅读者可以根据代码的情况自己画图证明:
说明:代码为dart语言实现,dart语法基本与Java一致,不清楚的地方可以参考:
https://www.dartlang.org/guides/language/language-tour
// 删除
bool delete(E value) {
var node = find(value);
if (node == null) return false;
_delete(node);
_nodeNumbers--;
return true;
} // 删除转移 并修复
void _delete(RBTNode<E> d) {
if (d.left != null && d.right != null) {
var s = _successor(d);
d.value = s.value;
d = s;
} var rp = d.left ?? d.right;
rp?.parent = d.parent;
if (d.parent == null)
_root = rp;
else if (d == d.parent.left)
d.parent.left = rp;
else
d.parent.right = rp; if (rp != null)
rp.paintBlack();
else if (d.isBlack && d.parent != null)
_fixAfterDelete(d.parent, d.parent.left == null);
} RBTNode<E> _successor(RBTNode<E> d) =>
d.right != null ? _minNode(d.right) : d.left; RBTNode<E> _minNode(RBTNode<E> r) => r.left == null ? r : _minNode(r.left); // fix up after delete
void _fixAfterDelete(RBTNode<E> p, bool isLeft) {
var ch = isLeft ? p.right : p.left;
if (isLeft) { // 如果被删除节点是父节点p的左分支;
if (p.isRed) { // 如果父节点为红,则兄弟节点ch一定为黑;
if (ch.left != null && ch.left.isRed) {
p.paintBlack();
_rotateRight(ch);
}
_rotateLeft(p);
} else if (ch.isRed) { // 兄弟节点为红,此时兄弟节点一定有两个非空黑色子节点;
p.paintRed();
ch.paintBlack();
_rotateLeft(p);
_fixAfterDelete(p, true); // 变换为父节点为红的情况,递归处理;
} else if (ch.left != null && ch.left.isRed) { // 父、兄均为黑,兄有红色左孩子;
ch.left.paintBlack();
_rotateRight(ch);
_rotateLeft(p);
} else { // 父兄均为黑,将父分支左右均减少一个黑节点,然后递归向上处理;
p.paintRed();
_rotateLeft(p);
if (ch.parent != null) _fixAfterDelete(ch.parent, ch == ch.parent.left);
}
} else { // symmetric
if (p.isRed) {
if (ch.right != null && ch.right.isRed) {
p.paintBlack();
_rotateLeft(ch);
}
_rotateRight(p);
} else if (ch.isRed) {
p.paintRed();
ch.paintBlack();
_rotateRight(p);
_fixAfterDelete(p, false);
} else if (ch.right != null && ch.right.isRed) {
ch.right.paintBlack();
_rotateLeft(ch);
_rotateRight(p);
} else {
p.paintRed();
_rotateRight(p);
if (ch.parent != null) _fixAfterDelete(ch.parent, ch == ch.parent.left);
}
}
}
旋转操作的代码:
void _rotateLeft(RBTNode<E> node) {
var r = node.right, p = node.parent;
r.parent = p;
if (p == null)
_root = r;
else if (p.left == node)
p.left = r;
else
p.right = r; node.right = r.left;
r.left?.parent = node;
r.left = node;
node.parent = r;
} void _rotateRight(RBTNode<E> node) {
var l = node.left, p = node.parent;
l.parent = p;
if (p == null)
_root = l;
else if (p.left == node)
p.left = l;
else
p.right = l; node.left = l.right;
l.right?.parent = node;
l.right = node;
node.parent = l;
}
红黑树的删除详解与思路分析——不同于教科书上的算法(dart语言实现)的更多相关文章
- stl map底层之红黑树插入步骤详解与代码实现
转载注明出处:http://blog.csdn.net/mxway/article/details/29216199 本篇文章并没有详细的讲解红黑树各方面的知识,只是以图形的方式对红黑树插入节点需要进 ...
- POJ-2590-Steps题目详解,思路分析及代码,规律题,重要的是找到规律~~
Steps Time Limit: 1000MS Memory Limit: 65536K http://poj.org/problem?id=2590 Description One ...
- RB-Tree删除详解
红黑树的删除操作较于插入操作,情况更为复杂: 考虑到红黑节点的差异性,我们在此通过红黑节点来考虑这个问题,即仅仅通过要删除的节点是红节点,还是黑节点来讨论不同的情况: 1 删除的红节点为叶子结点(此 ...
- LinkedList详解-源码分析
LinkedList详解-源码分析 LinkedList是List接口的第二个具体的实现类,第一个是ArrayList,前面一篇文章已经总结过了,下面我们来结合源码,学习LinkedList. 基于双 ...
- 关于syslog日志功能详解 事件日志分析、EventLog Analyzer
关于syslog日志功能详解 事件日志分析.EventLog Analyzer 一.日志管理 保障网络安全 Windows系统日志分析 Syslog日志分析 应用程序日志分析 Windows终端服务器 ...
- Java性能分析之线程栈详解与性能分析
Java性能分析之线程栈详解 Java性能分析迈不过去的一个关键点是线程栈,新的性能班级也讲到了JVM这一块,所以本篇文章对线程栈进行基础知识普及以及如何对线程栈进行性能分析. 基本概念 线程堆栈也称 ...
- 使用IDEA详解Spring中依赖注入的类型(上)
使用IDEA详解Spring中依赖注入的类型(上) 在Spring中实现IoC容器的方法是依赖注入,依赖注入的作用是在使用Spring框架创建对象时动态地将其所依赖的对象(例如属性值)注入Bean组件 ...
- Block详解二(底层分析)
Block专辑: Block讲解一 MRC-block与ARC-block Block详解一(底层分析) 今天讲述Block的最后一篇,后两篇仅仅是加深1,2篇的理解,废话少说,开始讲解! __blo ...
- ArrayList详解-源码分析
ArrayList详解-源码分析 1. 概述 在平时的开发中,用到最多的集合应该就是ArrayList了,本篇文章将结合源代码来学习ArrayList. ArrayList是基于数组实现的集合列表 支 ...
随机推荐
- ZooKeeper 的读写操作 & 选举机制
0. 说明 记录 ZooKeeper 的读写操作和选举机制 1. ZooKeeper 的读写操作 读操作:所有 ZooKeeper 节点都可以提供读请求(包括 follower 和 leader ) ...
- DLL导出类避免地狱问题的完美解决方案
DLL动态链接库是程序复用的重要方式,DLL可以导出函数,使函数被多个程序复用,DLL中的函数实现可以被修改而无需重新编译和连接使用该DLL的应用程序.作为一名面向对象的程序员,希望DLL可以导出类, ...
- [WPF]DropShadowEffect导致Image模糊问题
实现鼠标在图片上时,图片外侧有发光效果,如上图 可使用触发器修改Image控件的Effect属性 <Style.Triggers> <Trigger Property="I ...
- excle函数
1.time函数 说明: https://support.office.com/zh-cn/article/time-%E5%87%BD%E6%95%B0-9a5aff99-8f7d-4611-845 ...
- SDN 第三次上机作业
SDN 第三次上机作业 1.创建拓扑 2.利用OVS命令下发流表,实现vlan功能 3.利用OVS命令查看流表 s1: s2: 4.验证性测试 5.Wireshark 抓包验证
- ffmpeg常用参数一览表
基本选项: -formats 输出所有可用格式 -f fmt 指定格式(音频或视频格式) -i filename 指定输入文件名,在linux下当然也能指定:0.0(屏幕录制)或摄像头 -y 覆盖已有 ...
- webstorm 搭建es6开发环境
本文转自:http://www.jianshu.com/p/26601581e152 1:新建一个Empty Project项目es6 ,然后在src目录下新建了一个es.js: 2:打开设置pref ...
- python第二十七课——os模块
演示os模块中常用的属性和函数: 1.os模块: 作用:管理文件和目录 属性: os.name:返回系统类型 常用的windows系统 --> nt os.environ:返回当前系统所有的环境 ...
- 晚上打开eclipse时碰到这个问题 :Workspace in use or cannot be created, choose a different one.
晚上打开eclipse时碰到这个问题 :Workspace in use or cannot be created, choose a different one. 网上看到这方面的解决方式: 原因: ...
- luogu P1891 疯狂LCM
嘟嘟嘟 这题跟上一道题有点像,但是我还是没推出来--菜啊 \[\begin{align*} ans &= \sum_{i = 1} ^ {n} \frac{i * n}{gcd(i, n)} ...