RBtree插入跟删除图解代码
一、红黑树的简单介绍 RBT
红黑树是一种平衡的二叉查找树。是一种计算机科学中经常使用的数据结构,最典型的应用是实现数据的关联,比如map等数据结构的实现。
红黑树有下面限制:
1. 节点必须是红色或者是黑色
2. 根节点是黑色的
3. 全部的叶子节点是黑色的。
4. 每一个红色节点的两个子节点是黑色的,也就是不能存在父子两个节点全是红色
5. 从随意每一个节点到其每一个叶子节点的全部简单路径上黑色节点的数量是同样的。
要说明一下限制3。一般在红黑树中,每一个节点空指针处还会加空的黑色的孩子(称为岗哨)。
所以限制3一定成立。
另外,我们以下说到的叶子节点指的是原本的叶子,而不是NIL节点。
二、红黑树的插入
主要參考了:RBT
能够对着看。
1 当树还没有节点。插入一个新节点,依据限制2,当然是黑色咯~
2 给一个节点插入子节点,尽量把插入节点置成红色,由于置成黑色绝对要违反限制5。当看完“删除”就会发现黑色深度差导致的调整绝对要比颜色不符的调整要麻烦。所以我们会尽量避开。
2.1 当父节点是黑色的,子节点仅仅要置成红色。不违反1-4,也不影响父节点的两个子树的黑色深度,既不违反5。
2.2 当父节点是红色的。考虑到“调整深度差比較麻烦”,我们还是先把子节点置成红色。之后開始调整树。
红黑树插入调整的关键是要考虑叔叔节点。
在总结一下我们的问题:就是要解决“父”节点和“子”节点连续红色的问题,我们称之为“双红问题”。
闭目30秒,想到怎么调整吗?
小tips:事实上红黑树的调整都挺复杂并且无规律的,要记住也非常难。
但调整的做法无非是一下几种
1改变某个点的颜色;
2交换相邻点的颜色;
3左旋,右旋(见3.1)。
2.2.1 红叔
G改成红色,P、U改成黑色。
因为P和U的子节点都是黑色的。所以变色后也不会违反限制4。并且也不会违反限制5。
这样做有个问题,就是G变成红色。但G的父节点也可能是红色的,假设这样,事实上又是一个双红问题。
2.2.2 黑叔
乍看之下,天然的黑叔问题是不存在的,可是红叔问题不是可能会转化成双红问题,这时就可能出现黑叔的情况。
黑叔要旋转,有两种情况
case 1:右旋,再交换P和G的颜色
case 2:之后按case1右旋就可以
因为笔者比較懒。照搬其他博客的图。
事实上还应该画出P的右孩子。有兴趣的能够自己分析case1和case2为什么不会违反限制4,5
三、红黑树的删除(准备知识)
因为删除比較复杂。先写准备知识垫一垫。
1 旋转
第一招:把弯的变成直的
第二招:左旋、右旋:基本的作用就是较深的子树让出一些深度给较浅的子树
2 一般查找二叉树删除节点
删除的方案有非常多,但一般都会选以下这样的,由于对整棵树各个分支深度的影响较小。
a.当被删除节点n是叶子节点,直接删除
b.当被删除节点n仅仅有一个孩子,删除n,用孩子替代该节点的位置
c.当被删除结点n存在左右孩子时。真正的删除点应该是n的中序遍在前驱,或者说是左子树最大的节点,之后n的值替换为真正删除点的值。
这就把c归结为a,b的问题。
3 由2可知,全部的删除问题都能够转化成删除叶子节点或单支节点(仅仅有一个孩子)的问题
四、红黑树的删除
1 当删除节点n是红色的叶子节点,直接删除节点n。不影响红黑树平衡性质
2 当删除节点n是红色的单支节点。不可能出现,假设孩子是红色,违反限制4;假设孩子是黑色的,违反限制5.
3 当删除节点n是黑色的叶子节点,因为有岗哨的存在。能够转化为问题4 当删除节点为黑色而其后尾随红色 则将红色变为
黑色就可以。
4 当删除节点n是黑色的单支节点。既n有一个黑色的子节点。
例如以下图,先说明一下记号,删表示被删除节点n,子表示其子节点,父。兄都非常好理解了。黑色和红色表示真实的颜色,青色表示不确定的颜色。
首先我们非常大胆地直接删掉节点n。让子接替他的位置。
我们瞻前顾后地看一下,首先红黑树的删除问题的全部情况都讨论到了,可是有一个问题,就是4中这样删除会使“父”节点左子树的黑色深度比右子树少1。
所以以下我们要解决的不是删除问题,而是一个红黑树的调整选转问题。要求是这种,父节点的左孩子有一个黑色的节点,并且父节点的左子树黑色深度比右子树小1,要求调整它,使之满足红黑树限制。因为这个问题源于黑节点n有黑的子节点,我们称其为“双黑问题”。
4.1 红兄
x的兄弟w为红色,则w的儿子必定全黑,w父亲p也为黑。
改变p与w的颜色,同一时候对p做一次左旋。这样就将情况1转变为情况2,3,4的一种
4.2, 4.3,4.4 统称为黑兄问题
4.2 黑兄二黑侄 又分为4.2.1和4.2.2
4.2.1 黑兄二黑侄红父
p变成黑色,w变成红色。解决这个问题
4.2.2 黑兄二黑侄黑父
由于x子树相对于其兄弟w子树少一个黑色节点,能够将w置为红色,这样。x子树与w子树黑色节点一致。保持了平衡。
假设p有兄弟。它的黑色深度就会比兄弟小1,这样4.2.2又转化成为了一个双黑问题,规约为1-4的情况。
4.3 黑兄左红侄右黑侄
w为黑色,w左孩子红色,右孩子黑色。
交换w与左孩子的颜色。对w进行右旋。转换为情况4
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzE4MjE2NzU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" style="border:none; max-width:602px; max-height:100%; height:auto">
4.3 黑兄右红侄
因为是双黑问题的子情况,从左图可看出,“子”子树和“1”、“2”子树的黑色深度是一样的。
所以调整后的子树是满足红黑树的限制的。(既新“父”子树两側黑色深度相等。新兄子树也是如此)
五、总结
最后分析一下复杂度,红黑树可以以O(log2 n) 的时间复杂度进行搜索、插入、删除操作。
分析性能上总要有对照,和AVL树,跳表、B+树,散列表对照。
B+树:B+树按块存取的操作更适合外存。
红黑树和AVL树在目的上是相似的,都是平衡的二叉查找树,时间复杂度也一样,但统计性能比AVL树更高。
以下先说说hash和红黑树:
跳表:也是一种有序的,伸缩性较好的查找结构;和红黑树的性能就更像了。性能上不太懂,就不乱说了。
首先,一開始的删除操作与二叉查找树的类似。然后。假设删去的节点是红色的话,红黑树的性质并没有被破坏,可是假设删去的节点是黑色的话,红黑树的性质被破坏了,因此
须要又一次改动树的结构,使之满足红黑树的性质。如果节点删去后。该节点的儿子x替换了它原来的位置。
如今。如果x是其父节点的左儿子。由于删掉的是黑节点,那么有p[x]往左走遇到的黑节点数量要比p[x]往右走遇到的黑节点少一个。
假设x是红色的。那么简单的将x的颜色改为黑色就可以。
假设x是黑色的,情况就复杂了。设w是x的兄弟节点,分四种情况进行讨论:
Case1:w是红色的,那么运行一些操作使x的兄弟节点变为黑色的,即将Case1转变为Case2,3或4.
Case2:w是黑色的,且w的左、右儿子都是黑色的,此时将w变为红色的。即,使p[x]往右走遇到的黑节点数量也少了一个,这样
p[x]往左走、往右走遇到的黑节点数量就都同样
了,可是也导致p[p[x]]往p[x]那条路上走遇到的黑节点数量少了一个,因此。还是须要改动进一步树的结构,使之满足红黑树的性质。将x指向p[x],循环下去就可以。
Case3: w是黑色的。且w的左儿子是红色的、右儿子是黑色的。此时,运行一些操作。使w的右儿子变成红色的,即将Case3转换为Case4。
Case4: w是黑色的。且w的右儿子是红色的。此时,运行一些操作,使得变换后,往x那边走遇到的黑节点的数量添加一个,这样红黑树的性质也保持了。
上面说的“运行一些操作”,就是改变了一些节点的颜色,进行左旋啊右旋啊什么的,详细的能够看书,算法导论上的图还是非常清晰的。
template<typename T>
void RBTree<T>::DeleteReblance(RBNode<T> *node)
{
RBNode<T> *nodeparent = NULL;
RBNode<T> *w = NULL;
while(node->color==_rb_black_node && node->parent)
{
nodeparent = node->parent;
if(node == nodeparent->left)
{
w = nodeparent->right;
if(w->color==_rb_red_node)
{//情形1兄弟节点为红
nodeparent->color = _rb_red_node;
w->color = _rb_black_node;
_rbtree_rotate_left(nodeparent);
w = nodeparent->right; //将1转化为2 3 4 情况
}
if( (w->left==NULL || w->left->color==_rb_black_node)
&& (w->right==NULL || w->right->color==_rb_black_node))
{//情形2兄弟为黑,且兄弟的两个孩子也为黑
if(w->parent->color == RED)
{
w->color = _rb_red_node;
w->parent->color = BLACK;
}
else
{
w->color=_rb_red_node;
node = nodeparent;
nodeparent = nodeparent->parent;
}
}
else
{
if( w->right==NULL || w->right->color==_rb_black_node)
{//情形3兄弟节点的右孩子为黑,左为红
w->left->color=_rb_black_node;//此时左孩子一定存在且颜色为红,假设不满足就不会进入这个条件
w->color = _rb_red_node;
_rbtree_rotate_right(w);
w = nodeparent->right;// 转为情况4
}
//情形4兄弟节点的右孩子为红
w->right->color=_rb_black_node;
w->color = nodeparent->color;
nodeparent->color = _rb_black_node;
_rbtree_rotate_left(nodeparent);
break;
}
}
else
{
w = nodeparent->left;
if(w->color==_rb_red_node)
{//情形1兄弟节点为红
nodeparent->color = _rb_red_node;
w->color = _rb_black_node;
_rbtree_rotate_right(nodeparent);
w = nodeparent->left;
}
if( (w->left==NULL || w->left->color==_rb_black_node)
&& (w->right==NULL || w->right->color==_rb_black_node))
{//情形2兄弟为黑。且兄弟的两个孩子也为黑
if(w->parent->color == RED)
{
w->color = _rb_red_node;
w->parent->color = BLACK;
}
else
{
w->color=_rb_red_node;
node = nodeparent;
nodeparent = nodeparent->parent;
}
}
else
{
if( w->left==NULL || w->left->color==_rb_black_node)
{//情形3兄弟节点的右孩子为黑,左为红
w->right->color=_rb_black_node;//此时左孩子一定存在且颜色为红。假设不满足就不会进入这个条件
w->color = _rb_red_node;
_rbtree_rotate_left(w);
w = nodeparent->left;
}
//情形4兄弟节点的右孩子为红
w->left->color=_rb_black_node;
w->color = nodeparent->color;
nodeparent->color = _rb_black_node;
_rbtree_rotate_right(nodeparent);
break;
}
}
}
if(node)
{
node->color = _rb_black_node;
}
}
关于红黑树最全面的代码能够看 红黑树最全代码
RBtree插入跟删除图解代码的更多相关文章
- 红黑树插入与删除完整代码(dart语言实现)
之前分析了红黑树的删除,这里附上红黑树的完整版代码,包括查找.插入.删除等.删除后修复实现了两种算法,均比之前的更为简洁.一种是我自己的实现,代码非常简洁,行数更少:一种是Linux.Java等源码版 ...
- [改善Java代码]频繁插入和删除时使用LinkedList
一.分析 前面有文章分析了列表的表里方式,也就是“读”的操作.本文将介绍表的“写”操作:即插入.删除.修改动作. 二.场景 1.插入元素 列表中我们使用最多的是ArrayList,下面看看他的插入(a ...
- AVL树(查找、插入、删除)——C语言
AVL树 平衡二叉查找树(Self-balancing binary search tree)又被称为AVL树(AVL树是根据它的发明者G. M. Adelson-Velskii和E. M. Land ...
- 高级数据结构---红黑树及其插入左旋右旋代码java实现
前面我们说到的二叉查找树,可以看到根结点是初始化之后就是固定了的,后续插入的数如果都比它大,或者都比它小,那么这个时候它就退化成了链表了,查询的时间复杂度就变成了O(n),而不是理想中O(logn), ...
- Entity Framework 6 Recipes 2nd Edition(10-9)译 -> 在多对多关系中为插入和删除使用存储过程
10-9. 在多对多关系中为插入和删除使用存储过程 问题 想要在一个无载荷的多对多关系中使用存储过程(存储过程只影响关系的连接表) 解决方案 假设有一个多对多关系的作者( Author)表和书籍( B ...
- 数据结构Java实现03----单向链表的插入和删除
文本主要内容: 链表结构 单链表代码实现 单链表的效率分析 一.链表结构: (物理存储结构上不连续,逻辑上连续:大小不固定) 概念: 链式存储结构是基于指针实现的.我们把一个数据 ...
- 二叉查找树的查找、插入和删除 - Java实现
http://www.cnblogs.com/yangecnu/p/Introduce-Binary-Search-Tree.html 作者: yangecnu(yangecnu's Blog on ...
- Swift字符串的插入、删除和替换-备
对应可变字符串可以插入.删除和替换,String提供了几个方法可以帮助实现这些操作.这些方法如下: splice(_:atIndex:).在索引位置插入字符串. insert(_:atIndex:). ...
- Javascript进阶篇——(DOM—节点---插入、删除和替换元素、创建元素、创建文本节点)—笔记整理
插入节点appendChild()在指定节点的最后一个子节点列表之后添加一个新的子节点.语法: appendChild(newnode) //参数: //newnode:指定追加的节点. 为ul添加一 ...
随机推荐
- 1、初学探讨PYTHON的itchat和wxpy两库
最近好奇学习了python,觉得简单明了,但是最头疼的就是调整空格和调试吧,的确调试不如C#使用visual studio 方便,都是使用print()来调试.也许因为我是菜鸟,如果大家还有更好的方法 ...
- Python编程快速上手--实践项目11.11.1
from selenium import webdriver from selenium.webdriver.common.keys import Keys import time def messa ...
- 【HDU 2126】Buy the souvenirs(01背包)
When the winter holiday comes, a lot of people will have a trip. Generally, there are a lot of souve ...
- 七、docker基本命令
Docker 基本命令 docker的基本命令 docker version :查看docker的版本号,包括客户端.服务端.依赖的Go等 [root@centos7 ~]# docker versi ...
- appium+python自动化-adb logcat查看日志
前言 做app测试,遇到异常情况,查看日志是必不可少的,日志如何输出到手机sdcard和电脑的目录呢?这就需要用logcat输出日志了 以下操作是基于windows平台的操作:adb logcat | ...
- 脑阔疼的双层SQLserver游标
本来简单的双层游标没啥的,内层游标需要读取的是视图的内容,一直报“当前命令发生了严重错误.应放弃任何可能产生的结果.”的错误.无可奈何尝试先将视图的数据放到表变量中,之后再用游标遍历表变量. 简直很怀 ...
- 【C#】C#数据类型和VB的区别
导读:看完了C#,需要总结的有很多东西.开始没有怎么在意,根本没有意识到,那些视频是教给了我一种新的编程语言,我就真的是像看电视剧一样的看完了.猛然想起了学过的VB,这是目前为止,我接触到的仅有的语言 ...
- 九度oj 题目1499:项目安排
题目描述: 小明每天都在开源社区上做项目,假设每天他都有很多项目可以选,其中每个项目都有一个开始时间和截止时间,假设做完每个项目后,拿到报酬都是不同的.由于小明马上就要硕士毕业了,面临着买房.买车.给 ...
- 【bzoj1444】[Jsoi2009]有趣的游戏 AC自动机+矩阵乘法
题目描述 输入 注意 是0<=P 输出 样例输入 样例输出 题解 AC自动机+矩阵乘法 先将所有字符串放到AC自动机中,求出Trie图. 然后构建邻接矩阵:如果x不是某个字符串的末位置,则x连向 ...
- FZU 2020 :组合 【lucas】
Problem Description 给出组合数C(n,m), 表示从n个元素中选出m个元素的方案数.例如C(5,2) = 10, C(4,2) = 6.可是当n,m比较大的时候,C(n,m)很大! ...