我们一起讨论红黑树吧...
前一段看了很多的红黑树的文章,终于对红黑树算是有了简单的理解,今天趁着可以清闲的时间抓紧写一下,文章里面的图是我先在纸上画的,然后又用visio画了一遍,我的介绍中可能还会有错误,欢迎纠正和分享经验。
一、下面先简单介绍一下红黑树:
红黑树是一棵自*衡的二叉搜索树,他具备二叉搜索树的所有性质,同时由于其*衡性所以可以保证红黑树插入、查找和删除的效率,红黑树的用途相当大,主要是在实现一些关联数组的数据结构中起到了重要的作用,比如C++中的关联容器set和map以及Java的TreeMap结构等。
红黑树的五个性质:
1、每个节点要么是红色要么是黑色。
2、根节点是黑色。
3、NIL节点(空节点)是黑色。
4、如果一个节点是红色那么他的两个孩子节点都是黑色。(即同一条路径上不存在两个相邻的红色节点)。
5、从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
由于红黑树的这些性质,可以保证红黑树的**衡性,同时这些性质也导致了在对红黑树进行插入和删除时为了保证红黑树的特性造成了困难,在进行插入和删除之后就需要进行一些调整操作(左旋,右旋和着色)。
二、左旋和右旋。
关于左旋的示意图如下:
图2.1 左旋操作
这张图的左旋操作是以P为轴进行左旋操作,操作后B变为新的根结点,Br变为P的左孩子节点。
代码如下:
1 if(P->parent != NULL) 2 { 3 B->parent = P->parent; 4 if(P == P->parent->left) 5 { 6 P->parent->left = B; 7 } 8 else 9 { 10 P->parent->right = B; 11 } 12 } 13 14 if(B->left!= NULL) 15 { 16 B->left->parent = P; 17 } 18 19 P->parent = B; 20 P->right= B->left; 21 22 B->left = P;
关于右旋的示意图如下:
图2.1 右旋操作
由于右旋和左旋操作类似,这里就不细说了。
三、红黑树的插入
红黑树的插入主要有四种情况,下面针对这四种情况进行说明。
首先做一下统一规定,插入的节点设为N,父节点设为P,祖父节点设为G,叔叔节点设为U,并且由于插入黑节点必定会导致一中我说的红黑树性质5的破坏,所以插入节点置为红色。
3.1、插入节点的父节点P为黑色。
如下图所示
图3.1 插入情况1
由于父节点为黑色所以插入节点后的新树已然满足红黑树的五条性质,所以不用进行调整,直接插入即可。
下面关于红黑树插入的几种情况中父节点的颜色均为红色且只考虑父节点是祖父节点的左孩子的情况,由于父节点是祖父节点的右孩子的情况和父节点是左孩子的情况对称,就不描述了。
3.2、父节点和叔父节点都是红色。
做如下图所示转换
图3.1 插入情况2
即将父节点和叔父节点置为黑色色,祖父节点置为红色。
操作代码如下
1 U->color = BLACK; 2 P->color = BLACK; 3 G->color = RED; 4 N = G;
最后由于祖父G变为了红色可能继续导致树的性质破坏,所以需要对G做为继续操作的节点。
3.3、父节点红色,叔父节点黑色,且插入节点N是父节点的右孩子(因为在插入之前该树是*衡的,所以此处U应该为NIL节点即空节点,为了直观画成非空节点)。
需要做如下图所示转换:
图3.3 插入情况3
即以P为轴进行一次左旋操作。
操作代码如下:
1 rotate_left(P); 2 tmp = P; 3 P = N; 4 N = tmp;
由于左旋后N,P互换位置了,所以需要交换N,P(但是对树中不改变,只是为了继续进行修改红黑树操作时的更新N,P),然后将原来的P作为新的N进行下面的情况4的操作。
3.4、N的叔节点U是黑色,并且N是P的左孩子。(可能是由情况3变化而来的,因为在插入之前该树是*衡的,所以此处U应该为NIL节点即空节点,为了直观画成非空节点)
需要做如下图所示变换
图3.4 插入情况4
即P,G颜色互换,然后以G为轴进行一次右旋操作。
操作代码如下:
1 P->color = BLACK; 2 G->color = RED; 3 rotate_right(gparent);
由于当父节点P是祖父节点G的右孩子节点时和P是G的左孩子节点对称,同样是这三种情况,只不过旋转方向相反,所以就不描述了,至此红黑树的插入操作已经介绍完了,下面介绍红黑树的删除操作。
四、红黑树的删除
红黑树的删除主要有五种情况,下面针对这五种情况进行说明。
和插入一样首先做一下统一规定,删除的节点设为N,父节点设为P,,兄弟节点为B,兄弟节点的左孩子BL,右孩子BR。
首先需要说明的是红黑树额删除操作和二叉搜索树的删除操作类似,当需要删除的节点含有两个孩子节点时需要找到替代节点(左子树的最右孩子或右子树的最左孩子)作为真正的删除节点,然后用真正删除节点的值替换原来需要删除节点的值,同时只有当真正删除节点(替代节点或当原来要删除的节点不含有两个孩子时就是原来要删除的节点)为黑色的情况时才需要进行调整操作,因为删除红色节点不破坏红黑树的性质,故不需要调整。
4.1、N的兄弟节点B是红色
需要做下图所示转换
图4.1 删除情况1
即B,P换色,然后以P为轴进行一次左旋。
操作代码如下:
1 B->color = BLACK; 2 P->color = RED; 3 rotate_left(p); 4 B = P->right;
由于左旋后N的兄弟节点换成了BL所以需要进行更新B的操作。
4.2、N的兄弟为黑色,且兄弟节点B的两个孩子也是黑色的,并且N的父节点P也是黑色。
需要做如下如所示转换
图4.2 删除情况2
即将B着红色即可。
操作代码如下:
1 B->color = RED; 2 N = P; 3 P = N->parent;
由于修改了兄弟节点B的颜色,可能会导致N的祖父节点的子树不*衡,所以需要将N更新为P,然后继续对P做判断调整。
4.3、N的兄弟节点B是黑色,B的两个儿子也是黑色的,但N的父节点P是红色的。
需要做如下图所示转换
图4.3 删除情况3
只需要交换P,B的颜色。然后转到情况1
操作代码如下:
1 B->color = RED; 2 P->color = BLACK;
4.4、N的兄弟节点B是黑色,B的左孩子是红色,右孩子是黑色的
需要做如下图所示转换:
图4.4 删除情况4
即B,BL交换颜色,然后以B为轴进行一次右旋,然后转到下面的情况5处理。
操作代码如下:
1 BL->color = BLACK; 2 B->color = RED; 3 rotate_right(B); 4 B = P->right;
因为右旋之后N的兄弟节点换成了BL所以需要更新B为BL,然后再跳到下面的额情况5进行最后的处理。
4.5、N的兄弟节点B是黑色,且B的右孩子是红色的
需要做如下图所示转换
图4.5 删除情况5(P分别为黑色和红色的两种情况)
情况5可能由情况4转变而来,即将B的颜色变为P的颜色,P,BR变为黑色,然后再以P为轴进行一次左旋,左旋后将左边删除的黑色节点补充过来,这个子树就又满足了红黑树的5哥性质。
操作代码如下:
1 B->color = P->color; 2 P->color = BLACK; 3 BR->color = BLACK; 4 rorate_left(P);
当N是父节点P的右孩子时和前面介绍的N是父节点P的左孩子情况对称,只不过旋转方向变换成对称即可,所以不描述了。
至此关于红黑树的插入和删除操作后的重新调整*衡的几种情况已经全部说完,希望发现问题的读者积极纠正,一起进步。
下面是我之前看过的一些关于红黑树的博客,对我对红黑树的理解有了很大的帮助,大家也可以关注这些博主,他们讲解的知识真的很有帮助:
http://blog.csdn.net/chenssy/article/details/26668941
http://blog.csdn.net/v_july_v/article/details/6124989
http://blog.csdn.net/v_july_v/article/details/6114226
http://blog.csdn.net/v_JULY_v/article/details/6105630
http://www.cnblogs.com/fanzhidongyzby/p/3187912.html