《算法导论》学习记录目录html
红黑树是"平衡的"二叉查找树(PS: 关于二叉查找树的内容见连接)。普通的二叉查找树,在最坏状况下基本操做的运行时间为Θ(n),大部分的操做的最坏状况运行时间与树的高度成正比,对于相同的数据如何构造出一棵高效的二叉查找树(即高度尽可能小)(以下图)算法
图a为高效的二叉查找树,图b为低效的二叉查找树ide
形成图b中的二叉查找树低效的缘由在于建树的时候结点插入的顺序不恰当,选取关键字为2的结点(最小关键字结点)做为根结点使得全部的结点都位于根结点的右子树里。函数
更糟糕是全部的结点都在同一条路上,这时候就至关于在链表上进行操做。学习
可是普通的二叉查找树不会自动调整,使本身的尽可能平衡(根结点的左右子树的高度接近相同),它只会根据插入结点的关键字来判断插入的位置。为了使其能够接近平衡,咱们能够在结点里添加一些属性,而后根据这些属性来动态调整树的形状。(例如红黑树添加了颜色属性,AVL树添加告终点高度属性。)spa
红黑树的基本性质:3d
颜色属性:颜色为红色(RED)或者黑色(BLACK)。指针
NIL结点:指向二叉查找数的外结点(叶子)指针,颜色为黑色;某些结点的父结点或者子结点指向NIL结点,至关于哨兵。(即为书中的nil[ T ])code
红黑性质:一、每一个结点的颜色为红色或者黑色htm
二、根结点的颜色为黑色
三、每一个叶结点都是黑色
四、若是一个结点是红色的,那么它的两个儿子都是黑色的
五、对于每一个结点,从该结点到其子孙结点的全部路径上包含相同数目的黑结点
黑高度:从该结点x出发(不包括该结点)到达叶结点的任意一条路径上,黑色结点的个数为该结点的黑色高度,用bh(x)
具体以下图所示(深色为黑色结点,浅色为红色结点):
下为红黑树结点结构体、红黑树结构体和NIL指针等基本实现
1 #define RED 1 2 #define BLACK 0 3 4 typedef struct RB_TreeNode { 5 int value; 6 int color; 7 struct RB_TreeNode * parent; 8 struct RB_TreeNode * left; 9 struct RB_TreeNode * right; 10 }RB_TreeNode; //红黑树结点结构体 11 12 typedef struct RB_Tree { 13 RB_TreeNode * root; 14 }RB_Tree; //红黑树结构体,根结点 15 16 RB_TreeNode NILL = { -1, BLACK, NULL, NULL, NULL }; 17 RB_TreeNode *NIL = &NILL; //NIL结点指针
一棵n个内结点的红黑树的高度最多为2lg(n+1),这是红黑树是一种好的查找树的缘由。(PS:证实见书本,本人太菜,看完不会用本身的话总结。。。因此就不证实)
所以查找、插入、找最小值、找出最大值、找出前趋、找出后继、删除动态集合操做在红黑树上能够在O(lgn)时间内完成。
结点的旋转
由于直接会以前二叉查找树的插入、删除的实现不能保证操做后的二叉树仍是红黑树,因此咱们添加旋转操做,同过旋转操做来使红黑树在插入、删除操做后还能保持自己的性质。
旋转分为左旋转、右旋转(以下图所示)
经过旋转,咱们能够修改结点的指针域使结点知足本身的需求。
下为左旋转、右旋转的实现
1 /* 2 * x结点上进行左旋转,y结点(x结点的右儿子)的左儿子成为x结点的新右儿子 3 * x结点成为y结点的左儿子的新父结点 4 * x结点的父结点成为y结点的新父结点,y结点成为x结点的新父结点 5 * x结点成为y结点的新左儿子 6 */ 7 void Left_Rotate(RB_Tree *T, RB_TreeNode *x){ 8 RB_TreeNode *y = x->right; //x点右儿子 9 x->right = y->left; //y结点的左儿子会成为x结点的右儿子 10 if(y->left != NIL) 11 y->left->parent = x; //若是y有左儿子,y的左儿子的父结点为x 12 y->parent = x->parent; 13 if(x->parent == NIL) 14 T->root = y; //若是x的父结点为哨兵,说明x为根结点,则y成为根结点 15 else if(x == x->parent->left) 16 x->parent->left = y; 17 else 18 x->parent->right = y; //判断x为其父结点的左、右儿子,y成为x父结点对应的儿子 19 y->left = x; //y的左儿子为x 20 x->parent = y; //x的父结点为y 21 } 22 23 /* 24 * x结点上进行右旋转,y结点(x结点的左儿子)的右儿子成为x结点的新左儿子 25 * x结点成为y结点的右儿子的新父结点 26 * x结点的父结点成为y结点的新父结点,y结点成为x结点的新父结点 27 * x结点成为y结点的新右儿子 28 * PS:代码的解释可参照左旋转 29 */ 30 void Right_Rotate(RB_Tree *T, RB_TreeNode *x){ 31 RB_TreeNode *y = x->left; 32 x->left = y->right; 33 if(y->right != NIL) 34 y->right->parent = x; 35 y->parent = x->parent; 36 if(x->parent == NIL) 37 T->root = y; 38 else if(x == x->parent->left) 39 x->parent->left = y; 40 else 41 x->parent->right = y; 42 y->right = x; 43 x->parent = y; 44 }
选择效果例子以下图:
有了旋转操做,咱们就能够进行插入和删除操做。红黑树的插入和删除,是基于普通二叉查找树的插入和删除,再添加维持红黑性质的操做(PS:二叉查找树的插入、删除操做见以前的博客)。
插入
当新的结点插入到红黑树时,咱们将该结点的颜色设置为红色,这样红黑树的性质1(没有结点存在其余颜色)、3(新结点的左右儿子为NIL,黑色结点)、5(添加红色结点不影响黑色结点的数目)没改变,可是有性质2(新结点有可能为根结点)和性质4(新结点的父结点有可能也是红色的)。PS:该段中括号为对应的解释。。。。
插入新结点后,若是它不是根结点并且它的父结点不是红色的,那么就无需作有关调整。可是若是不符合红黑性质的时候就须要作有关调整,若是其为根结点,直接将其颜色设置为黑色便可。除此以外,还存在3种状况不符合红黑性质(PS:当前的状况可能通过一次调整后成为其余状况。具体来说应该有6种状况,不过有3种和另外3种对称,下文是考虑z结点的父结点为左儿子):
状况1:结点z的叔叔是红色。这样咱们直接将z父亲的父亲颜色变为红色,z的父亲和叔叔的颜色变为黑色(这样性质5没有改变),最后将z父亲的父亲做为新的z结点,继续上移,直到z结点的父结点为黑色或者z为根结点(当z为根结点,直接将其颜色改成黑色,这样整个红黑树就比原来多了一个黑色结点。可是改变颜色前已经通过各类调整,只违反性质2,改变颜色后,就没有违反任何红黑性质)。以下图所示:
状况2:结点z的叔叔是黑色的、并且z是右儿子(状况2能够经过旋转成为状况3)
状况3:结点z的叔叔是黑色的、并且z是左儿子(PS:若是z结点的父结点为右儿子的时候状况2要改成“z是左儿子”,状况3要改成“z是右儿子”)由于z的叔叔是黑色,因此不能够直接经过改变颜色来维持红黑性质,要经过旋转操做来维持。以下图所示,若是为状况2就经过对A进行左旋转变为状况3,再改变结点的颜色,最后将C进行右旋转。这样就使得解决违反性质4,并且不违反性质5.(PS:若是z结点的父结点为右儿子的时候,应将左旋转改成右旋转,右旋转改成左旋转。具体画图理一理就明白)
下为插入和插入调整实现(PS:插入的注释见二叉查找树的插入)
1 /* 2 * 插入函数,注意插入结点与其在树中对应的父结点的连接(须要记录父结点)。 3 * 从根结点出发,不停用当前结点与插入的值比较,若是当前结点的值比较大就往当前结点的左儿子走,相反就往右儿子走,直到当前结点为空, 4 * 在过程当中记录当前结点的父结点。 5 * 运行时间为O(h),h为树的高度。由于整个过程是一条沿着根结点降低的路径。 6 */ 7 void RB_Insert(RB_Tree *T, int key){ 8 RB_TreeNode *z; 9 z = (RB_TreeNode *)malloc(sizeof(RB_TreeNode)); //新建结点 10 z->value = key; 11 z->color = RED; 12 z->parent = z->left = z->right = NIL; 13 RB_TreeNode *x = T->root; 14 RB_TreeNode *y = NIL; 15 while(x != NIL){ 16 y = x; 17 if(x->value < key) 18 x = x->right; 19 else 20 x = x->left; 21 } 22 z->parent = y; 23 if(y == NIL){ 24 T->root = z; 25 T->root->parent = NIL; 26 T->root->parent->color = BLACK; 27 } 28 else if(z->value < y->value) 29 y->left = z; 30 else 31 y->right = z; 32 RB_Insert_Fixup(T, z); //插入调整,维持红黑性质 33 } 34 35 /* 36 * 插入调整,维持红黑性质 37 */ 38 void RB_Insert_Fixup(RB_Tree *T, RB_TreeNode *z){ 39 while(z->parent->color == RED){ //若是z结点的父结点为黑色,就不用进入3种状况的处理 40 if(z->parent == z->parent->parent ->left){ //z的父结点为左儿子的状况 41 RB_TreeNode *y = z->parent->parent->right; 42 if(y->color == RED){ //状况1 43 z->parent->color = BLACK; 44 y->color = BLACK; 45 z->parent->parent->color = RED; 46 z = z->parent->parent; 47 } 48 else { 49 if(z == z->parent->right){ //状况2 50 z = z->parent; 51 Left_Rotate(T, z); //左旋转,状况2变为3 52 } 53 z->parent->color = BLACK; //状况3 54 z->parent->parent->color = RED; 55 Right_Rotate(T, z->parent->parent); //右选择解决违反性质4 56 } 57 } 58 else{ 59 RB_TreeNode *y = z->parent->parent->left; //z的结点为右儿子的状况 60 if(y->color == RED){ //状况1 61 z->parent->color = BLACK; 62 y->color = BLACK; 63 z->parent->parent->color = RED; 64 z = z->parent->parent; 65 } 66 else{ 67 if(z == z->parent->left){ //状况2 68 z = z->parent; 69 Right_Rotate(T, z); //右旋转,状况2变为状况3 70 } 71 z->parent->color = BLACK; //状况3 72 z->parent->parent->color = RED; 73 Left_Rotate(T, z->parent->parent); //左旋转解决违反性质4 74 } 75 } 76 } 77 T->root->color = BLACK; //若是红黑树T的根结点为红色就会变成黑色,若是是黑色变成黑色也没影响 78 }
红黑树的插入操做总共花费时间为O(lgn)。由于插入部分花费O(lgn),而插入调整只有状况1执行的时候才可能要重复进行while循环,所以while循环可能的运行次数为O(lgn)。因此总共花费为O(lgn)。
删除(PS:下文的z为想删除的结点、y结点为实际要删除的结点、x结点为y的惟一孩子结点。若是以为想删除结点、实际删除结点、实际删除结点的惟一孩子感到困惑,请看二叉查找树,里面有解释,虽然里面的命名又点不同。。。)
对于删除结点,若是删除的是红色的结点,对红黑性质没有任何影响。可是若是删除的是黑色结点,就会产生3个问题:一、若是y是根结点,而y的一个红色孩子成为了根结点,违反了性质2;二、若是x和y的父结点都是红色,违反性质4;三、删除y致使包含y的任何路径黑结点个数少1,违反了性质5,解决这个问题的办法是x添加多一层黑色(双重黑色或红黑),这样就由违反性质5变为违反性质1。(PS:若是要恢复性质5会很麻烦。。。。这个我是看网友博客了。。还没探究这个问题)
对于删除调整如何恢复性质二、4:删除调整实现里对于x为根结点或者x的颜色为红色时直接将x结点的颜色变成黑色。若是x为根结点,直接改变为黑色便可,若是x不为根结点,将x变成黑色,能够解决违反性质4,并且以前包含y的任何路径的黑色结点个数也恢复。(这个是我本身看完伪代码想的,书中要求读者说明删除调整代码如何恢复二、4,因此可能有错,欢迎指出错误)。
删除调整主要将额外的黑色沿树上移,直到:一、x指向一个红黑结点,直接将x的颜色变为黑色
二、x指向根,直接消除额外的黑色
三、或者作必要的旋转和颜色修改
总共8状况(4种与另外4种对称,下文是考虑x为左儿子的状况)(下文的x结点为须要调整的结点,即增长了额外一层黑色)
状况1:x的兄弟w是红色。先改变w和x的父结点的颜色,对x的父结点进行一次作旋转,红黑性质继续维持,可是咱们经过这些操做使得状况1转换成状况二、三、4
状况2:x的兄弟w是黑色,并且w的两个孩子都是黑色。x和w的去掉一重黑色,那么x只有一重黑色,而w变为红色(由于w的两个儿子都是黑色因此变成红色没有违反性质4)。而后咱们再在x的父结点上添加一层额外的黑色,最后x的父结点成为新的x结点继续我和循环。
状况3:x的兄弟w是黑色的,w的左孩子是红色,右孩子是黑色的。首先交换w和其左孩子的颜色,对w进行右旋转。这样就从状况3转换成状况4.
状况4:x的兄弟w是黑色的,并且w的右孩子是红色的。首先交换w和x的父结点的颜色,对x的父结点进行左旋转,去掉x的额外黑色,又不破坏红黑性质。最后将x置为根结点,接受循环。
(PS:x为右儿子的状况时,须要将上文的左旋转换成右旋转,右旋转换成左旋转)
具体以下图所示
下为删除和删除调整实现:
1 /* 2 * 删除结点函数,首先要肯定真正删除的结点是那个。 3 * 若是z没有子结点,直接将z的父结点对应的指针指向NULL 4 * 若是z只有一个子节点,直接将z的父结点对应的指针指向z的子结点 5 * 若是z有两个子结点,实际上要删除的不是z,而是z的后继,,再用z的后继的内容代替z的内容 6 */ 7 8 void RB_Delete(RB_Tree *T, RB_TreeNode *z){ 9 RB_TreeNode *x = NULL; 10 RB_TreeNode *y = NULL; 11 12 if(z->left == NIL || z->right == NIL) 13 y = z; 14 else 15 y = RB_Tree_Successor(z); 16 if(y->left != NIL) 17 x = y->left; 18 else 19 x = y->right; 20 x->parent = y->parent; 21 if(y->parent == NIL) 22 T->root = x; 23 else if(y == y->parent->left) 24 y->parent->left = x; 25 else 26 y->parent->right = x; 27 if(y != z) 28 z->value = y->value; 29 if(y->color == BLACK) 30 RB_Delete_Fixup(T, x); //当实际要删除的结点y为黑色时才须要进行调整 31 } 32 33 /* 34 * 删除调整,使红黑树维持红黑性质 35 */ 36 void RB_Delete_Fixup(RB_Tree *T, RB_TreeNode *x){ 37 while(x != T->root && x->color == BLACK){ //当x结点为根结点或者x的颜色为红色(即为红黑, 实际上额外黑色没有直接加上去,只是默认x节点有一重额外的黑色) 38 if(x == x->parent->left){ //x为左儿子状况 39 RB_TreeNode *w = x->parent->right; 40 if(w->color == RED){ //状况1 41 w->color = BLACK; 42 x->parent->color = RED; 43 Left_Rotate(T, x->parent); //左旋转,使得状况1转换成状况二、3或4 44 w = x->parent->right; 45 } 46 47 if(w->left->color == BLACK && w->right->color == BLACK){ //状况2 48 w->color = RED; //修改颜色 49 x = x->parent; //上移 50 } 51 else{ 52 if(w->right->color == BLACK){ //状况3 53 w->left->color = BLACK; 54 w->color = RED; 55 Right_Rotate(T, w); //右旋转,状况3转换成状况4 56 w = x->parent->right; 57 } 58 w->color = x->parent->color; 59 x->parent->color = BLACK; 60 w->right->color = BLACK; 61 Left_Rotate(T, x->parent); //左旋转,去掉x的额外黑色 62 x = T->root; //使循环结束 63 } 64 65 } 66 else{ //x为右儿子状况(PS:下文的注释参考上文) 67 RB_TreeNode *w = x->parent->left; 68 if(w->color == RED){ 69 w->color = BLACK; 70 x->parent->color = RED; 71 Right_Rotate(T, x->parent); 72 w = x->parent->left; 73 } 74 75 if(w->left->color == BLACK && w->right->color == BLACK){ 76 w->color = RED; 77 x = x->parent; 78 } 79 else { 80 if(w->left->color == BLACK){ 81 w->right->color = BLACK; 82 w->color = RED; 83 Left_Rotate(T, w); 84 w = x->parent->left; 85 } 86 w->color = x->parent->color; 87 x->parent->color = BLACK; 88 w->left->color = BLACK; 89 Right_Rotate(T, x->parent); 90 x = T->root; 91 } 92 } 93 } 94 }
删除操做的总运行时间为O(lgn)。
下为所有代码(除了删除、插入实现的注释见二叉查找树)
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 #define RED 1 5 #define BLACK 0 6 7 typedef struct RB_TreeNode { 8 int value; 9 int color; 10 struct RB_TreeNode * parent; 11 struct RB_TreeNode * left; 12 struct RB_TreeNode * right; 13 }RB_TreeNode; //红黑树结点结构体 14 15 typedef struct RB_Tree { 16 RB_TreeNode * root; 17 }RB_Tree; //红黑树结构体,根结点 18 19 RB_TreeNode NILL = { -1, BLACK, NULL, NULL, NULL }; 20 RB_TreeNode *NIL = &NILL; //NIL结点指针 21 22 void Left_Rotate(RB_Tree *T, RB_TreeNode *x); 23 24 void Right_Rotate(RB_Tree *T, RB_TreeNode *x); 25 26 void RB_Insert(RB_Tree *T, int key); 27 28 void RB_Insert_Fixup(RB_Tree *T, RB_TreeNode *z); 29 30 void RB_Delete(RB_Tree *T, RB_TreeNode *z); 31 32 void RB_Delete_Fixup(RB_Tree *T, RB_TreeNode *x); 33 34 RB_TreeNode * RB_Search(RB_TreeNode *x, int key); 35 36 RB_TreeNode * RB_Tree_Minimum(RB_TreeNode *x); 37 38 RB_TreeNode * RB_Tree_Maximum(RB_TreeNode *x); 39 40 RB_TreeNode * RB_Tree_Successor(RB_TreeNode *x); 41 42 RB_TreeNode * RB_Tree_Predecesor(RB_TreeNode *x); 43 44 void Inorder_RB_Tree_Walk(RB_TreeNode *x); 45 46 void free_men(RB_TreeNode *x); 47 48 int main(){ 49 RB_Tree *T; 50 T->root = NIL; 51 52 int n, value, i; 53 scanf("%d", &n); 54 for(i = 1; i<= n; i++){ 55 scanf("%d", &value); 56 RB_Insert(T, value); 57 Inorder_RB_Tree_Walk(T->root); 58 printf("\n"); 59 } 60 RB_TreeNode *s = RB_Search(T->root, 3); 61 if(s != NIL) 62 printf("%d is exists!\n", s->value); 63 /*Inorder_RB_Tree_Walk(T->root); 64 65 printf("\n");*/ 66 printf("%d\n", RB_Tree_Minimum(T->root)->value); 67 printf("%d\n", RB_Tree_Maximum(T->root)->value); 68 printf("%d\n", RB_Tree_Successor(s)->value); 69 printf("%d\n", RB_Tree_Predecesor(s)->value); 70 RB_Delete(T, s); 71 Inorder_RB_Tree_Walk(T->root); 72 printf("\n"); 73 return 0; 74 } 75 76 /* 77 * x结点上进行左旋转,y结点(x结点的右儿子)的左儿子成为x结点的新右儿子 78 * x结点成为y结点的左儿子的新父结点 79 * x结点的父结点成为y结点的新父结点,y结点成为x结点的新父结点 80 * x结点成为y结点的新左儿子 81 */ 82 void Left_Rotate(RB_Tree *T, RB_TreeNode *x){ 83 RB_TreeNode *y = x->right; //x点右儿子 84 x->right = y->left; //y结点的左儿子会成为x结点的右儿子 85 if(y->left != NIL) 86 y->left->parent = x; //若是y有左儿子,y的左儿子的父结点为x 87 y->parent = x->parent; 88 if(x->parent == NIL) 89 T->root = y; //若是x的父结点为哨兵,说明x为根结点,则y成为根结点 90 else if(x == x->parent->left) 91 x->parent->left = y; 92 else 93 x->parent->right = y; //判断x为其父结点的左、右儿子,y成为x父结点对应的儿子 94 y->left = x; //y的左儿子为x 95 x->parent = y; //x的父结点为y 96 } 97 98 /* 99 * x结点上进行右旋转,y结点(x结点的左儿子)的右儿子成为x结点的新左儿子 100 * x结点成为y结点的右儿子的新父结点 101 * x结点的父结点成为y结点的新父结点,y结点成为x结点的新父结点 102 * x结点成为y结点的新右儿子 103 * PS:代码的解释可参照左旋转 104 */ 105 void Right_Rotate(RB_Tree *T, RB_TreeNode *x){ 106 RB_TreeNode *y = x->left; 107 x->left = y->right; 108 if(y->right != NIL) 109 y->right->parent = x; 110 y->parent = x->parent; 111 if(x->parent == NIL) 112 T->root = y; 113 else if(x == x->parent->left) 114 x->parent->left = y; 115 else 116 x->parent->right = y; 117 y->right = x; 118 x->parent = y; 119 } 120 121 /* 122 * 插入函数,注意插入结点与其在树中对应的父结点的连接(须要记录父结点)。 123 * 从根结点出发,不停用当前结点与插入的值比较,若是当前结点的值比较大就往当前结点的左儿子走,相反就往右儿子走,直到当前结点为空, 124 * 在过程当中记录当前结点的父结点。 125 * 运行时间为O(h),h为树的高度。由于整个过程是一条沿着根结点降低的路径。 126 */ 127 void RB_Insert(RB_Tree *T, int key){ 128 RB_TreeNode *z; 129 z = (RB_TreeNode *)malloc(sizeof(RB_TreeNode)); //新建结点 130 z->value = key; 131 z->color = RED; 132 z->parent = z->left = z->right = NIL; 133 RB_TreeNode *x = T->root; 134 RB_TreeNode *y = NIL; 135 while(x != NIL){ 136 y = x; 137 if(x->value < key) 138 x = x->right; 139 else 140 x = x->left; 141 } 142 z->parent = y; 143 if(y == NIL){ 144 T->root = z; 145 T->root->parent = NIL; 146 T->root->parent->color = BLACK; 147 } 148 else if(z->value < y->value) 149 y->left = z; 150 else 151 y->right = z; 152 RB_Insert_Fixup(T, z); //插入调整,维持红黑性质 153 } 154 155 /* 156 * 插入调整,维持红黑性质 157 */ 158 void RB_Insert_Fixup(RB_Tree *T, RB_TreeNode *z){ 159 while(z->parent->color == RED){ //若是z结点的父结点为黑色,就不用进入3种状况的处理 160 if(z->parent == z->parent->parent ->left){ //z的父结点为左儿子的状况 161 RB_TreeNode *y = z->parent->parent->right; 162 if(y->color == RED){ //状况1 163 z->parent->color = BLACK; 164 y->color = BLACK; 165 z->parent->parent->color = RED; 166 z = z->parent->parent; 167 } 168 else { 169 if(z == z->parent->right){ //状况2 170 z = z->parent; 171 Left_Rotate(T, z); //左旋转,状况2变为3 172 } 173 z->parent->color = BLACK; //状况3 174 z->parent->parent->color = RED; 175 Right_Rotate(T, z->parent->parent); //右选择解决违反性质4 176 } 177 } 178 else{ 179 RB_TreeNode *y = z->parent->parent->left; //z的结点为右儿子的状况 180 if(y->color == RED){ //状况1 181 z->parent->color = BLACK; 182 y->color = BLACK; 183 z->parent->parent->color = RED; 184 z = z->parent->parent; 185 } 186 else{ 187 if(z == z->parent->left){ //状况2 188 z = z->parent; 189 Right_Rotate(T, z); //右旋转,状况2变为状况3 190 } 191 z->parent->color = BLACK; //状况3 192 z->parent->parent->color = RED; 193 Left_Rotate(T, z->parent->parent); //左旋转解决违反性质4 194 } 195 } 196 } 197 T->root->color = BLACK; //若是红黑树T的根结点为红色就会变成黑色,若是是黑色变成黑色也没影响 198 } 199 200 /* 201 * 删除结点函数,首先要肯定真正删除的结点是那个。 202 * 若是z没有子结点,直接将z的父结点对应的指针指向NULL 203 * 若是z只有一个子节点,直接将z的父结点对应的指针指向z的子结点 204 * 若是z有两个子结点,实际上要删除的不是z,而是z的后继,,再用z的后继的内容代替z的内容 205 */ 206 207 void RB_Delete(RB_Tree *T, RB_TreeNode *z){ 208 RB_TreeNode *x = NULL; 209 RB_TreeNode *y = NULL; 210 211 if(z->left == NIL || z->right == NIL) 212 y = z; 213 else 214 y = RB_Tree_Successor(z); 215 if(y->left != NIL) 216 x = y->left; 217 else 218 x = y->right; 219 x->parent = y->parent; 220 if(y->parent == NIL) 221 T->root = x; 222 else if(y == y->parent->left) 223 y->parent->left = x; 224 else 225 y->parent->right = x; 226 if(y != z) 227 z->value = y->value; 228 if(y->color == BLACK) 229 RB_Delete_Fixup(T, x); //当实际要删除的结点y为黑色时才须要进行调整 230 } 231 232 /* 233 * 删除调整,使红黑树维持红黑性质 234 */ 235 void RB_Delete_Fixup(RB_Tree *T, RB_TreeNode *x){ 236 while(x != T->root && x->color == BLACK){ //当x结点为根结点或者x的颜色为红色(即为红黑, 实际上额外黑色没有直接加上去,只是默认x节点有一重额外的黑色) 237 if(x == x->parent->left){ //x为左儿子状况 238 RB_TreeNode *w = x->parent->right; 239 if(w->color == RED){ //状况1 240 w->color = BLACK; 241 x->parent->color = RED; 242 Left_Rotate(T, x->parent); //左旋转,使得状况1转换成状况二、3或4 243 w = x->parent->right; 244 } 245 246 if(w->left->color == BLACK && w->right->color == BLACK){ //状况2 247 w->color = RED; //修改颜色 248 x = x->parent; //上移 249 } 250 else{ 251 if(w->right->color == BLACK){ //状况3 252 w->left->color = BLACK; 253 w->color = RED; 254 Right_Rotate(T, w); //右旋转,状况3转换成状况4 255 w = x->parent->right; 256 } 257 w->color = x->parent->color; 258 x->parent->color = BLACK; 259 w->right->color = BLACK; 260 Left_Rotate(T, x->parent); //左旋转,去掉x的额外黑色 261 x = T->root; //使循环结束 262 } 263 264 } 265 else{ //x为右儿子状况(PS:下文的注释参考上文) 266 RB_TreeNode *w = x->parent->left; 267 if(w->color == RED){ 268 w->color = BLACK; 269 x->parent->color = RED; 270 Right_Rotate(T, x->parent); 271 w = x->parent->left; 272 } 273 274 if(w->left->color == BLACK && w->right->color == BLACK){ 275 w->color = RED; 276 x = x->parent; 277 } 278 else { 279 if(w->left->color == BLACK){ 280 w->right->color = BLACK; 281 w->color = RED; 282 Left_Rotate(T, w); 283 w = x->parent->left; 284 } 285 w->color = x->parent->color; 286 x->parent->color = BLACK; 287 w->left->color = BLACK; 288 Right_Rotate(T, x->parent); 289 x = T->root; 290 } 291 } 292 } 293 } 294 295 296 RB_TreeNode * RB_Search(RB_TreeNode *x, int key){ 297 if(x->value == key || x == NIL) 298 return x; 299 if(x->value > key) 300 RB_Search(x->left, key); 301 else 302 RB_Search(x->right, key); 303 } 304 305 RB_TreeNode * RB_Tree_Minimum(RB_TreeNode *x){ 306 RB_TreeNode *r = x; 307 while(r->left != NIL) 308 r = r->left; 309 return r; 310 } 311 312 RB_TreeNode * RB_Tree_Maximum(RB_TreeNode *x){ 313 RB_TreeNode *r = x; 314 while(r->left != NIL) 315 r = r->left; 316 return r; 317 } 318 319 RB_TreeNode * RB_Tree_Successor(RB_TreeNode *x){ 320 RB_TreeNode *r = x; 321 if(r->right != NIL) 322 return RB_Tree_Minimum(r->right); 323 RB_TreeNode *y = r->parent; 324 while(y != NIL && r == y->right){ 325 r = y; 326 y = y->parent; 327 } 328 return y; 329 } 330 RB_TreeNode * RB_Tree_Predecesor(RB_TreeNode *x){ 331 RB_TreeNode *r = x; 332 if(r->left != NIL) 333 return RB_Tree_Maximum(r->left); 334 RB_TreeNode *y = r->parent; 335 while(y != NIL && r == y->left){ 336 r = y; 337 y = y->parent; 338 } 339 return y; 340 } 341 342 void Inorder_RB_Tree_Walk(RB_TreeNode *x){ 343 if(x != NIL){ 344 Inorder_RB_Tree_Walk(x->left); 345 printf("%d : ", x->value); 346 if(x->color == 1) 347 printf("red "); 348 else 349 printf("black "); 350 Inorder_RB_Tree_Walk(x->right); 351 } 352 } 353 354 void free_men(RB_TreeNode *x){ 355 if(x != NIL){ 356 free_men(x->left); 357 free_men(x->right); 358 free(x); 359 } 360 }
PS:吐槽来了。。。之后要多更新博客,打算把当天学到有感受的东西记录下来。。。算导可能更新会有点慢(已经两个星期才一篇。。。太弱菜了,越到最后要好好磨)。。。打算写SICP学习记录和Linux学校记录。。。。。好好努力吧!!!博客能更新真开心。。。。。