红黑树详解

1.为何须要红黑树?html

对于二叉搜索树,若是插入的数据是随机的,那么它就是接近平衡的二叉树,平衡的二叉树,它的操做效率(查询,插入,删除)效率较高,时间复杂度是O(logN)。可是可能会出现一种极端的状况,那就是插入的数据是有序的(递增或者递减),那么全部的节点都会在根节点的右侧或左侧,此时,二叉搜索树就变为了一个链表,它的操做效率就下降了,时间复杂度为O(N),因此能够认为二叉搜索树的时间复杂度介于O(logN)和O(N)之间,视状况而定。那么为了应对这种极端状况,红黑树就出现了,它是具有了某些特性的二叉搜索树,能解决非平衡树问题,红黑树是一种接近平衡的二叉树。node

2.红黑树的特性有哪些?spa

首先,红黑树是一个二叉搜索树,它同时知足如下特性:3d

(1) 每一个节点要么是黑色,要么是红色code

(2) 根节点是黑色htm

(3) 若是节点是红色的,那么它的子节点必须是黑色的(反之,不必定须要成立)blog

(4) 从根节点到叶节点或空子节点的每条路径,都包含相同数目的黑色节ip

经过看图来理解以上四个特性get

 

3.红黑树的效率io

红黑树的查找,插入和删除操做,时间复杂度都是O(logN)。查找操做时,它和普通的相对平衡的二叉搜索树的效率相同,都是经过相同的方式来查找的,没有用到红黑树特有的特性。但,若是插入的时候是有序数据,那么红黑树的查询效率就比二叉搜索树要高了,由于此时二叉搜索树不是平衡树,它的时间复杂度O(N)。插入和删除操做时,因为红黑树的每次操做平均要旋转一次和变换颜色,因此它比普通的二叉搜索树效率要低一点,不过期间复杂度仍然是O(logN)。总之,红黑树的优势就是对有序数据的查询操做不会慢到O(logN)的时间复杂度。

4.对旋转的理解

在红黑树中,插入或者删除数据时,为了保持红黑树的那五个特性,须要进行旋转和变换颜色的操做。旋转必需要一次性作两件事情:

* 使一些节点上升,一些节点降低,帮助树平衡

* 保证不破坏二叉搜索树的特征

旋转分为左旋转和右旋转,那么咱们就看下左旋转和右旋转是怎么回事。

4.1 左旋转

此处,以50为支点进行逆时针旋转,而后75成为了顶点,50成为了75的左子节点,65成为了50的右子节点,这个操做就是左旋转。

4.2 右旋转

此处,以75为支点顺时针旋转,而后50成为了顶点,75成为了50的右子节点,65成为了75的左子节点,这就是右旋转操做。

5.插入操做

在介绍插入操做前,先约定一下各个节点的名称。看图:

在红黑树中,插入一个节点,都执行了哪些操做呢?

首先,查找要插入节点的位置,而后给予颜色(红色或者黑色),可是为了保证红黑树的特性,须要进行旋转或者更改颜色,须要注意的是,在旋转前和旋转后,红黑树一直都是一个二叉搜索树,二叉搜索树的特征从未改变过。

这里,对于新插入的节点,咱们给予的颜色是红色,是由于为了和红黑树的第(4)条特性不冲突: 从根节点到叶节点或空子节点的每条路径,都包含相同数目的黑色节点。这样就少了不少操做。而后看其它几个特性

* 对于(1)特性:每一个节点要么是黑色,要么是红色,不冲突。

* 对于(2)特性:根节点是黑色,若是插入的节点不是根节点,也不冲突。若是是根节点,那么直接给予颜色黑色

那么,惟一须要知足的特性就是(3):若是节点是红色的,那么它的子节点必须是黑色的。这里,当咱们给予新插入的节点颜色是红色时,须要根据父节点的不一样状况作不一样的处理,以知足这个特性。有如下几种状况:

根据代码来理解这几种状况:

/* 红黑树修正程序 */
void insert_repair_tree(struct node* n) {
//状况一:节点N的父节点为null,说明该节点是根节点
if (parent(n) == NULL) { insert_case1(n); } else if (parent(n)->color == BLACK) {
//状况二:父节点是黑色 insert_case2(n); }
else if (uncle(n)->color == RED) {
//状况三: 父节点和叔叔节点都是红色 insert_case3(n); }
else {
//状况四:父节点是红色,叔叔节点是黑色 insert_case4(n); } }

5.1:若是新插入节点是根节点,那么给予该节点颜色是黑色

看代码:

 

/* 状况一:插入的节点是根节点 */
void insert_case1(struct node* n)
{
 if (parent(n) == NULL)
  n->color = BLACK;
}

 

5.2:若是新插入节点的父节点是黑色,那么给予该节点是红色不会对该红黑树有任何影响,因此不作任何处理。

看代码:

/* 状况二:父节点是黑色 */
void insert_case2(struct node* n)
{
  return; /* Do nothing since tree is still valid */
}

5.3:若是新插入节点的父节点是红色,同时叔叔节点也是红色

       须要将父亲节点和叔叔节点从新绘制为黑色,祖父节点绘制为红色。可是,若是此处祖父节点为根节点,那么须要调用红黑树修正程序,将祖父节点绘制为黑色。

看代码:发现看代码比看图要更容易理解,而且逻辑也更严谨,对于父亲节点和叔叔节点都是红色时,发现其余文章都没有考虑祖父节点为根节点的状况,这里图就省了。

/* 状况三:父节点和叔叔节点都是红色 */
void insert_case3(struct node* n)
{
 parent(n)->color = BLACK;
 uncle(n)->color = BLACK;
 grandparent(n)->color = RED;
//调用红黑树修正程序 insert_repair_tree(grandparent(n)); }

5.4:若是新插入节点的父节点是红色,同时叔叔节点是黑色

看代码:

/* 状况四:第一步 */
void insert_case4(struct node* n)
{
 struct node* p = parent(n);
 struct node* g = grandparent(n);

 if (n == g->left->right) {
  /* 若是父节点是祖父节点的左子节点,新插入节点是父节点的右子节点,那么以父节点为支点左旋 */
  rotate_left(p);
  n = n->left;
 } else if (n == g->right->left) {
    /* 若是父节点是祖父节点的右子节点,新插入节点是父节点的左子节点,那么以父节点为支点右旋 */
  rotate_right(p);
  n = n->right; 
 }

 insert_case4step2(n);
}
/* 状况四:第二步 */
/* 在第二步时:当前节点n确定处于 祖父节点左子节点的左侧,或者祖父节点右子节点的右侧 */ void insert_case4step2(struct node* n) {
struct node* p = parent(n); struct node* g = grandparent(n); if (n == p->left)
/* 以祖父节点为支点进行右旋 */ rotate_right(g);
else rotate_left(g); p->color = BLACK; g->color = RED; }

在知足父节点是红色,叔叔节点是黑色的条件下,咱们能够分如下几种状况:

状况(a):父节点p是祖父节点g的左子节点,新插入节点n是父节点p的右子节点

处理:以父节点为支点左旋,而后以祖父节点为支点进行右旋,最后给父节点(第一次旋转后的父节点)绘制为黑色,给祖父节(第一次旋转后的祖父节点)点绘制为红色

看图:

状况(b):父节点p是祖父节点g的右子节点,新插入节点n是父节点p的左子节点

处理:以父节点为支点进行右旋,而后以祖父为支点进行左旋,最后给父节点(第一次旋转后的父节点)绘制为黑色,给祖父节(第一次旋转后的祖父节点)点绘制为红色

看图:

状况(c):父节点p是祖父节点g的左子节点,新插入节点n是父节点p的左子节点

处理:以祖父节点为支点进行右旋,最后给父节点绘制为黑色,给祖父节点绘制为红色,也就是直接调用第二步的方法 insert_case4step2

状况(d):父节点p是祖父节点g的右子节点,新插入节点n是父节点p的右子节点

处理:以祖父为支点进行左旋,最后给父节点绘制为黑色,给祖父节点绘制为红色,也就是直接调用第二步的方法 insert_case4step2

 6 删除操做

对于删除操做,处理的逻辑是:根据二叉搜索树的特色找到被删除的节点,将其删除掉,而后再经过改变颜色和旋转操做保持其红黑树的特性。此处咱们统一规定被删除节点是D,真正被删除节点为RD,真正被删除节点的兄弟节点是B,真正被删除节点的父节点是P,B的两个子节点是BL和BR。删除操做有三种状况:

6.1 被删除节点为叶子节点,分为两种状况:

状况(a):该叶子节点是红色

处理:直接删除

状况(b):该叶子节点是黑色,

处理:删除节点,而后旋转和变换颜色。

6.2 被删除节点只有一个子节点,那么使被删除节点的父节点指向被删除节点的子节点,而后经过改变节点颜色和旋转来保持红黑树特性

6.3 被删除节点D有两个子节点,那么能够将被删除节点D的后继节点(D的右子树中的最小元素RD)的值赋值给被删除节点D,可是被删除节点D的颜色不作改变,此时删除操做就转化为了删除后继节点RD的状况了。那么删除RD的状况又分为五种:

状况(a): RD节点是红色的

处理:若是RD节点是红色的,那么它的父节点和右子节点应该是黑色的,当删除RD节点时,直接把RD节点的父节点指向RD节点的右子节点便可。

下面四种状况RD节点都是黑色的

状况(b):RD的兄弟节点B为红色

处理:若是B节点为红色,那么P节点和B的两个子节点都是黑色。交换B节点和P节点的颜色,而后以P节点为支点进行左旋转。这样处理后,就将状况(b)转为(c),(d),(e)中的一种。

看图:

 

 状况(c):

 

后续更新。。。

 

 

参考资料:

模拟红黑树:https://www.cs.usfca.edu/~galles/visualization/RedBlack.html

维基百科红黑树:https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Insertion

相关文章
相关标签/搜索