文章图片来自邓俊辉老师课件

先提几个问题去思考学习本文 :

  • 红黑树和2-4树(B-Tree)很像,那么它存在的动机又是什么呢
  • 插入和删除操作的逻辑又是怎么样的,时间和空间复杂度可以达到怎么样
  • 和 AVL 对象有什么区别呢

概述

定义

我们可以看到红黑树有4条重要的定义,这4条定义保证了这个平衡树。下面我们看一下它和B-Tree的联系。

从这个结构上说,我们可以知道B-Tree相比于红黑树,红黑树需要维护一个颜色这样的属性,需要空间,而同时红黑树搜索时可以

像二叉树一般查找,而B-Tree每一个超级节点需要维护多个关键码。这方面查看 RST_WIKI 这里的分析。

但是这样的树是BBST吗?

上面的数学推算已经向我证明,平均的深度为 h = O(LogN)

动机

Persistant Structure 一致性

下面介绍了红黑树为什么适应一致性结构。

时间和空间复杂度红黑树可以适应条件。同时拓扑结构上,无论是插入还是删除,都可以不超过O(1).

最坏情况下保证插入和删除,查找

这里引用 wiki 上的一段话说明红黑树在这方面的表现。

Red–black trees offer worst-case guarantees for insertion time, deletion time, and search time. Not only does this make them valuable in time-sensitive applications such as real-time applications, but it makes them valuable building blocks in other data structures which provide worst-case guarantees; for example, many data structures used in computational geometry can be based on red–black trees, and the Completely Fair Scheduler used in current Linux kernels and epoll system call implementation[19] uses red–black trees.

时间空间复杂度

出处见参考资料

插入原理解析

我们定义插入的节点为红色,那么就有一种情况,双红,这违反了我们前面红黑树的定义,下面介绍如何解决双红问题。

RR-1

第一种情况 u 节点(uncle节点)是黑色的。

我们可以使用3+4实现,让树消除双红现象,3+4实现可以参考这篇文章 : 3+4

RR-2

u节点是红色的情况下,最终可能导致树高度+1

插入归纳

删除原理解析

我们思考一下,假如先不看颜色,那么二叉树的删除算法中,考察三种情况

  • 拥有其中一个子节点
  • 无子节点
  • 拥有左右子节点

其中第一和第二种情况很好处理,直接删除即可,有其中一个子节点需要重新连接一下父节点。第三种需要找出继承节点,然后替

换掉删除节点。继承节点简单点解释就是右树中最小的一个。

好了,那么此时我们再来思考一下颜色的问题,我们知道红黑树不能红红相联,且每个底部节点到根节点黑节点的数量都相等,要

是删除节点是上面第一种情况和第二种情况且删除节点是红节点,直接删除对树的平衡没有情况影响。

那么要是删除节点和继承节点一黑一白呢,我们只需将删除后,将继承节点染黑就可以了,见下图。

那么删除节点和继承节点都是黑的情况呢?

双黑缺陷

可以看到,当删除节点和替代节点都为黑节点,删除会产生下溢(下溢的概念可以参考上篇B-树),需要考察继承节点的父节点p和

兄弟节点 s ,下面分四种情况处理 :

BB-1

a’ 和 b’ 都是B-Tree 的扑拓结构,可以看到当s拥有一个红节点时,产生下溢的节点通过旋转,向兄弟节点借来了一个节点,从而

达到了平衡。而从 a 到 b 的过程,需要借助的是3+4操作。

BB-2R

BB-2R的情况,就相当于B-Tree的合并,而我们看到最终的扑拓结构是不变的,只需要进行染色,那么当父节点被拿走了一个,是否会产生下溢呢?不会,因为在父节点中有红节点,那么左或右必有黑节点。

BB-2B

同样也是合并操作,此时不是像BB-2R一样是红色节点了,那么就有可能引发下沉下溢,那么是不是会像AVL一样进行LogN次的旋转操作呢?不会,从a到b,我们可以他们的扑拓结构没有改变,改变的只是颜色,所以不会发生LogN次的旋转。时间复杂度依旧是O(1).

BB-3

可以看到,经过了左旋或是右旋,还有变色,由a到b后,黑高度依旧异常,可以有一个好消息就是,经过旋转变成了我们之前处理

的情况一样,即 BB-1 或是 BB-2R ,不会是 BB-2B的原因是 x 有个新的兄弟节点 s’ ,而且 p 为红节点。

至此,我们双黑的情况全部介绍完毕。

归纳总结和AVL的对比

代码实现

代码实现我们以java中的TreeMap 来解释。本文只会介绍删除操作。

   /**
* Removes the mapping for this key from this TreeMap if present.
*
* @param key key for which mapping should be removed
* @return the previous value associated with {@code key}, or
* {@code null} if there was no mapping for {@code key}.
* (A {@code null} return can also indicate that the map
* previously associated {@code null} with {@code key}.)
* @throws ClassCastException if the specified key cannot be compared
* with the keys currently in the map
* @throws NullPointerException if the specified key is null
* and this map uses natural ordering, or its comparator
* does not permit null keys
*/
public V remove(Object key) {
Entry<K,V> p = getEntry(key);
if (p == null)
return null; V oldValue = p.value;
deleteEntry(p);
return oldValue;
} /**
* Returns this map's entry for the given key, or {@code null} if the map
* does not contain an entry for the key.
*
* @return this map's entry for the given key, or {@code null} if the map
* does not contain an entry for the key
* @throws ClassCastException if the specified key cannot be compared
* with the keys currently in the map
* @throws NullPointerException if the specified key is null
* and this map uses natural ordering, or its comparator
* does not permit null keys
*/
final Entry<K,V> getEntry(Object key) {
// Offload comparator-based version for sake of performance
if (comparator != null)
return getEntryUsingComparator(key);
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
Entry<K,V> p = root;
while (p != null) {
int cmp = k.compareTo(p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
return null;
} /**
* Delete node p, and then rebalance the tree.
*/
private void deleteEntry(Entry<K,V> p) {
modCount++;
size--; // If strictly internal, copy successor's element to p and then make p
// point to successor.
if (p.left != null && p.right != null) {
Entry<K,V> s = successor(p);
p.key = s.key;
p.value = s.value;
p = s;
} // p has 2 children //到了这里,无论p是有几个孩子,p这个变量变成了要删除的节点
//要是 p有两个child,会进入上面那个if,p变为了继承节点
// Start fixup at replacement node, if it exists.
Entry<K,V> replacement = (p.left != null ? p.left : p.right); if (replacement != null) { //只存在一个子节点
// Link replacement to parent 重新连接父节点,需要删除的节点置为 null
replacement.parent = p.parent;
if (p.parent == null)
root = replacement;
else if (p == p.parent.left)
p.parent.left = replacement;
else
p.parent.right = replacement; // Null out links so they are OK to use by fixAfterDeletion.
p.left = p.right = p.parent = null; // Fix replacement 开始修复,判断是不是黑节点是因为红节点直接删除没有影响 :
// 每个底部节点到根节点的黑色节点数量相等
if (p.color == BLACK)
fixAfterDeletion(replacement);
} else if (p.parent == null) { // return if we are the only node. 该树只有一个节点
root = null;
} else { // No children. Use self as phantom replacement and unlink. 没有子节点
if (p.color == BLACK) //继承节点为黑
fixAfterDeletion(p); if (p.parent != null) { //继承节点为红,直接删除
if (p == p.parent.left)
p.parent.left = null;
else if (p == p.parent.right)
p.parent.right = null;
p.parent = null;
}
}
} // 实际就是解决双黑节点的问题
/** From CLR */
private void fixAfterDeletion(Entry<K,V> x) {
while (x != root && colorOf(x) == BLACK) { //非根且为黑节点
if (x == leftOf(parentOf(x))) {
Entry<K,V> sib = rightOf(parentOf(x)); //取右兄弟节点 if (colorOf(sib) == RED) {
setColor(sib, BLACK);
setColor(parentOf(x), RED);
rotateLeft(parentOf(x));
sib = rightOf(parentOf(x));
} if (colorOf(leftOf(sib)) == BLACK &&
colorOf(rightOf(sib)) == BLACK) {
setColor(sib, RED);
x = parentOf(x);
} else {
if (colorOf(rightOf(sib)) == BLACK) {
setColor(leftOf(sib), BLACK);
setColor(sib, RED);
rotateRight(sib);
sib = rightOf(parentOf(x));
}
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(rightOf(sib), BLACK);
rotateLeft(parentOf(x));
x = root;
}
} else { // symmetric //和前面是对称的
Entry<K,V> sib = leftOf(parentOf(x)); // x是左节点 if (colorOf(sib) == RED) { //假如是BB-3,旋转后只能是 BB-1 或者是 BB-2R
setColor(sib, BLACK);
setColor(parentOf(x), RED);
rotateRight(parentOf(x));
sib = leftOf(parentOf(x));
} if (colorOf(rightOf(sib)) == BLACK && //BB-2R 或是 BB-2B
colorOf(leftOf(sib)) == BLACK) {
setColor(sib, RED);
x = parentOf(x);
} else {
if (colorOf(leftOf(sib)) == BLACK) { //BB-1 中 红在右边
setColor(rightOf(sib), BLACK);
setColor(sib, RED);
rotateLeft(sib);
sib = leftOf(parentOf(x));
}
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(leftOf(sib), BLACK);
rotateRight(parentOf(x));
x = root; //跳出while
}
}
} setColor(x, BLACK);
}

参考资料

  • 邓俊辉老师数据结构课程
  • RST_WIKI

数据结构(四)--- 红黑树(RedBlock-Tree)的更多相关文章

  1. 手撸红黑树-Red-Black Tree 入门

    一.学习红黑树前的准备: 熟悉基础数据结构 了解二叉树概念 二.红黑树的规则和规则分析: 根节点是黑色的 所有叶子节点(Null)是黑色的,一般会认定节点下空节点全部为黑色 如果节点为红色,那么子节点 ...

  2. Linux 内核里的数据结构:红黑树(rb-tree)

    转自:https://www.cnblogs.com/slgkaifa/p/6780299.html 作为一种数据结构.红黑树可谓不算朴素.由于各种宣传让它过于神奇,网上搜罗了一大堆的关于红黑树的文章 ...

  3. 【数据结构】红黑树-Java实现

    WIKI:https://en.wikipedia.org/wiki/Red%E2%80%93black_tree 转:红黑树(五)之 Java的实现 总结的比较精炼的: http://www.cnb ...

  4. 关于红黑树(R-B tree)原理,看这篇如何

    学过数据数据结构都知道二叉树的概念,而又有多种比较常见的二叉树类型,比如完全二叉树.满二叉树.二叉搜索树.均衡二叉树.完美二叉树等:今天我们要说的红黑树就是就是一颗非严格均衡的二叉树,均衡二叉树又是在 ...

  5. 物联网安全himqtt防火墙数据结构之红黑树源码分析

    物联网安全himqtt防火墙数据结构之红黑树源码分析 随着5G的发展,物联网安全显得特别重要,himqtt是首款完整源码的高性能MQTT物联网防火墙 - MQTT Application FireWa ...

  6. [转]SGI STL 红黑树(Red-Black Tree)源代码分析

    STL提供了许多好用的数据结构与算法,使我们不必为做许许多多的重复劳动.STL里实现了一个树结构-Red-Black Tree,它也是STL里唯一实现的一个树状数据结构,并且它是map, multim ...

  7. 红黑树(Red-Black Tree),B树,B-树,B+树,B*树

    (一)红黑树(Red-Black Tree) http://www.cnblogs.com/skywang12345/p/3245399.html#a1 它一种特殊的二叉查找树.红黑树的每个节点上都有 ...

  8. Nginx数据结构之红黑树ngx_rbtree_t

    1. 什么是红黑树? 1.1 概述 红黑树实际上是一种自平衡二叉查找树. 二叉树是什么?二叉树是每个节点最多有两个子树的树结构,每个节点都可以用于存储数据,可以由任 1 个节点访问它的左右 子树或父节 ...

  9. BZOJ-3227 红黑树(tree) 树形DP

    个人认为比较好的(高端)树形DP,也有可能是人傻 3227: [Sdoi2008]红黑树(tree) Time Limit: 10 Sec Memory Limit: 128 MB Submit: 1 ...

  10. [Data Structure] 红黑树( Red-Black Tree ) - 笔记

    1.  红黑树属性:根到叶子的路径中,最长路径不大于最短路径的两倍. 2. 红黑树是一个二叉搜索树,并且有 a. 每个节点除了有左.右.父节点的属性外,还有颜色属性,红色或者黑色. b. ( 根属性 ...

随机推荐

  1. c# 用户输入一个字符串,求字符串的长度

    C#  用户输入一个字符串,求字符串的长度使用字符串的length: class Program { static void Main(string[] args) { Console.WriteLi ...

  2. 虚拟机ping 不通主机,主机可ping 虚拟机解决方法

    在VMware虚拟机里安装了CentOS的系统发现桥接模式Ping不通外网,Ping主机也ping 不通,但是主机可以ping 虚拟机. 百度了以下,原因是w10防火墙搞的鬼,解决办法有两种: 1.关 ...

  3. [USACO09FEB] 改造路Revamping Trails | [JLOI2011] 飞行路线

    题目链接: 改造路 飞行路线 其实这两道题基本上是一样的,就是分层图的套路题. 为什么是分层图呢?首先,我们的选择次数比较少,可以把这几层的图建出来而不会爆空间.然后因为选择一个边权为0的路线之后我们 ...

  4. 二十五、MongoDB 索引 和 explain 的使用

    一.索引基础 索引是对数据库表中一列或多列的值进行排序的一种结构,可以让我们查询数据库变得更快.MongoDB 的索引几乎与传统的关系型数据库一模一样,这其中也包括一些基本的查询优化技巧.创建索引的命 ...

  5. jquery函数封装

    <script type="text/javascript"> $(function () { $("#tabMenu a").on('click' ...

  6. python中package机制的两种实现方式

    当执行import module时,解释器会根据下面的搜索路径,搜索module1.py文件. 1) 当前工作目录 2) PYTHONPATH中的目录 3) Python安装目录 (/usr/loca ...

  7. Python3之文本操作

    文件操作示例分析: 文件操作一般要经历三个步骤: 打开文件 操作文件 关闭文件 读取操作示例: >>>f = open('test.txt', 'r') # 打开文件test.txt ...

  8. CH6201走廊泼水节

    题目链接: CH6201 [简化版题意]给定一棵N个节点的树,要求增加若干条边,把这棵树扩充为完全图,并满足图的唯一最小生成树仍然是这棵树.求增加的边的权值总和最小是多少. 输入格式 本题为多组数据~ ...

  9. Vs.net 常用命令行

    下面的是从 devenv /? 看到的   用法: devenv  [解决方案文件 | 项目文件 | 任意文件.扩展名]  [开关] devenv 的第一个参数通常是一个解决方案文件或项目文件. 如果 ...

  10. 3、TensorFlow基础(一) 设计思想与编程模型

    1.TensorFlow系统架构 如图为TensorFlow的系统架构图: TensorFlow的系统架构图,自底向上分为设备层和网络层.数据操作层.图计算层.API层.应用层,其中设备层和网络层,数 ...