红黑树(二)之 C语言的实现
来源:互联网 发布:zip解压软件mac版 编辑:程序博客网 时间:2024/05/29 15:14
出自:http://www.cnblogs.com/skywang12345/p/3624177.html
概要
红黑树在日常的使用中比较常用,例如Java的TreeMap和TreeSet,C++的STL,以及Linux内核中都有用到。之前写过一篇文章专门介绍红黑树的理论知识,本文将给出红黑数的C语言的实现代码,后序章节再分别给出C++和Java版本的实现。还是那句话,三种实现原理相同,择其一了解即可;若文章有错误或不足的地方,望不吝指出!
目录
1. 红黑树的介绍
2. 红黑树的C实现(代码说明)
3. 红黑树的C实现(完整源码)
4. 红黑树的C测试程序
转载请注明出处:http://www.cnblogs.com/skywang12345/p/3624177.html
更多内容:数据结构与算法系列 目录
(01) 红黑树(一)之 原理和算法详细介绍
(02) 红黑树(二)之 C语言的实现
(03) 红黑树(三)之 Linux内核中红黑树的经典实现
(04) 红黑树(四)之 C++的实现
(05) 红黑树(五)之 Java的实现
(06) 红黑树(六)之 参考资料
红黑树的介绍
红黑树(Red-Black Tree,简称R-B Tree),它一种特殊的二叉查找树。
红黑树是特殊的二叉查找树,意味着它满足二叉查找树的特征:任意一个节点所包含的键值,大于等于左孩子的键值,小于等于右孩子的键值。
除了具备该特性之外,红黑树还包括许多额外的信息。
红黑树的每个节点上都有存储位表示节点的颜色,颜色是红(Red)或黑(Black)。
红黑树的特性:
(1) 每个节点或者是黑色,或者是红色。
(2) 根节点是黑色。
(3) 每个叶子节点是黑色。 [注意:这里叶子节点,是指为空的叶子节点!]
(4) 如果一个节点是红色的,则它的子节点必须是黑色的。
(5) 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
关于它的特性,需要注意的是:
第一,特性(3)中的叶子节点,是只为空(NIL或null)的节点。
第二,特性(5),确保没有一条路径会比其他路径长出俩倍。因而,红黑树是相对是接近平衡的二叉树。
红黑树示意图如下:
红黑树的C实现(代码说明)
红黑树的基本操作是添加、删除和旋转。在对红黑树进行添加或删除后,会用到旋转方法。为什么呢?道理很简单,添加或删除红黑树中的节点之后,红黑树就发生了变化,可能不满足红黑树的5条性质,也就不再是一颗红黑树了,而是一颗普通的树。而通过旋转,可以使这颗树重新成为红黑树。简单点说,旋转的目的是让树保持红黑树的特性。
旋转包括两种:左旋 和 右旋。下面分别对旋转(左旋和右旋)、添加、删除进行介绍。
1. 基本定义
#define RED 0 // 红色节点#define BLACK 1 // 黑色节点typedef int Type;// 红黑树的节点typedef struct RBTreeNode{ unsigned char color; // 颜色(RED 或 BLACK) Type key; // 关键字(键值) struct RBTreeNode *left; // 左孩子 struct RBTreeNode *right; // 右孩子 struct RBTreeNode *parent; // 父结点}Node, *RBTree;// 红黑树的根typedef struct rb_root{ Node *node;}RBRoot;
RBTreeNode是红黑树的节点类,RBRoot是红黑树的根。
2. 左旋
对x进行左旋,意味着"将x变成一个左节点"。
左旋的实现代码(C语言)
/* * 对红黑树的节点(x)进行左旋转 * * 左旋示意图(对节点x进行左旋): * px px * / / * x y * / \ --(左旋)--> / \ # * lx y x ry * / \ / \ * ly ry lx ly * * */static void rbtree_left_rotate(RBRoot *root, Node *x){ // 设置x的右孩子为y Node *y = x->right; // 将 “y的左孩子” 设为 “x的右孩子”; // 如果y的左孩子非空,将 “x” 设为 “y的左孩子的父亲” x->right = y->left; if (y->left != NULL) y->left->parent = x; // 将 “x的父亲” 设为 “y的父亲” y->parent = x->parent; if (x->parent == NULL) { //tree = y; // 如果 “x的父亲” 是空节点,则将y设为根节点 root->node = y; // 如果 “x的父亲” 是空节点,则将y设为根节点 } else { if (x->parent->left == x) x->parent->left = y; // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子” else x->parent->right = y; // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子” } // 将 “x” 设为 “y的左孩子” y->left = x; // 将 “x的父节点” 设为 “y” x->parent = y;}
3. 右旋
对y进行左旋,意味着"将y变成一个右节点"。
右旋的实现代码(C语言)
/* * 对红黑树的节点(y)进行右旋转 * * 右旋示意图(对节点y进行左旋): * py py * / / * y x * / \ --(右旋)--> / \ # * x ry lx y * / \ / \ # * lx rx rx ry * */static void rbtree_right_rotate(RBRoot *root, Node *y){ // 设置x是当前节点的左孩子。 Node *x = y->left; // 将 “x的右孩子” 设为 “y的左孩子”; // 如果"x的右孩子"不为空的话,将 “y” 设为 “x的右孩子的父亲” y->left = x->right; if (x->right != NULL) x->right->parent = y; // 将 “y的父亲” 设为 “x的父亲” x->parent = y->parent; if (y->parent == NULL) { //tree = x; // 如果 “y的父亲” 是空节点,则将x设为根节点 root->node = x; // 如果 “y的父亲” 是空节点,则将x设为根节点 } else { if (y == y->parent->right) y->parent->right = x; // 如果 y是它父节点的右孩子,则将x设为“y的父节点的右孩子” else y->parent->left = x; // (y是它父节点的左孩子) 将x设为“x的父节点的左孩子” } // 将 “y” 设为 “x的右孩子” x->right = y; // 将 “y的父节点” 设为 “x” y->parent = x;}
4. 添加
将一个节点(z)插入到红黑树中,需要执行哪些步骤呢?首先,将红黑树当作一颗二叉查找树,将节点插入;然后,将节点着色为红色;最后,通过"旋转和重新着色"等一系列操作来修正该树,使之重新成为一颗红黑树。详细描述如下:
第一步: 将红黑树当作一颗二叉查找树,将节点插入。
红黑树本身就是一颗二叉查找树,将节点插入后,该树仍然是一颗二叉查找树。也就意味着,树的键值仍然是有序的。此外,无论是左旋还是右旋,若旋转之前这棵树是二叉查找树,旋转之后它一定还是二叉查找树。这也就意味着,任何的旋转和重新着色操作,都不会改变它仍然是一颗二叉查找树的事实。
好吧?那接下来,我们就来想方设法的旋转以及重新着色,使这颗树重新成为红黑树!
第二步:将插入的节点着色为"红色"。
为什么着色成红色,而不是黑色呢?为什么呢?在回答之前,我们需要重新温习一下红黑树的特性:
(1) 每个节点或者是黑色,或者是红色。
(2) 根节点是黑色。
(3) 每个叶子节点是黑色。 [注意:这里叶子节点,是指为空的叶子节点!]
(4) 如果一个节点是红色的,则它的子节点必须是黑色的。
(5) 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
将插入的节点着色为红色,不会违背"特性(5)"!少违背一条特性,就意味着我们需要处理的情况越少。接下来,就要努力的让这棵树满足其它性质即可;满足了的话,它就又是一颗红黑树了。o(∩∩)o...哈哈
第三步: 通过一系列的旋转或着色等操作,使之重新成为一颗红黑树。
第二步中,将插入节点着色为"红色"之后,不会违背"特性(5)"。那它到底会违背哪些特性呢?
对于"特性(1)",显然不会违背了。因为我们已经将它涂成红色了。
对于"特性(2)",显然也不会违背。在第一步中,我们是将红黑树当作二叉查找树,然后执行的插入操作。而根据二叉查找数的特点,插入操作不会改变根节点。所以,根节点仍然是黑色。
对于"特性(3)",显然不会违背了。这里的叶子节点是指的空叶子节点,插入非空节点并不会对它们造成影响。
对于"特性(4)",是有可能违背的!
那接下来,想办法使之"满足特性(4)",就可以将树重新构造成红黑树了。
添加操作的实现代码(C语言)
/* * 添加节点:将节点(node)插入到红黑树中 * * 参数说明: * root 红黑树的根 * node 插入的结点 // 对应《算法导论》中的z */static void rbtree_insert(RBRoot *root, Node *node){ Node *y = NULL; Node *x = root->node; // 1. 将红黑树当作一颗二叉查找树,将节点添加到二叉查找树中。 while (x != NULL) { y = x; if (node->key < x->key) x = x->left; else x = x->right; } rb_parent(node) = y; if (y != NULL) { if (node->key < y->key) y->left = node; // 情况2:若“node所包含的值” < “y所包含的值”,则将node设为“y的左孩子” else y->right = node; // 情况3:(“node所包含的值” >= “y所包含的值”)将node设为“y的右孩子” } else { root->node = node; // 情况1:若y是空节点,则将node设为根 } // 2. 设置节点的颜色为红色 node->color = RED; // 3. 将它重新修正为一颗二叉查找树 rbtree_insert_fixup(root, node);}
rbtree_insert(root, node)的作用是将"node"节点插入到红黑树中。其中,root是根,node是被插入节点。
rbtree_insert(root, node)是参考《算法导论》中红黑树的插入函数的伪代码进行实现的。
添加修正操作的实现代码(C语言)
/* * 红黑树插入修正函数 * * 在向红黑树中插入节点之后(失去平衡),再调用该函数; * 目的是将它重新塑造成一颗红黑树。 * * 参数说明: * root 红黑树的根 * node 插入的结点 // 对应《算法导论》中的z */static void rbtree_insert_fixup(RBRoot *root, Node *node){ Node *parent, *gparent; // 若“父节点存在,并且父节点的颜色是红色” while ((parent = rb_parent(node)) && rb_is_red(parent)) { gparent = rb_parent(parent); //若“父节点”是“祖父节点的左孩子” if (parent == gparent->left) { // Case 1条件:叔叔节点是红色 { Node *uncle = gparent->right; if (uncle && rb_is_red(uncle)) { rb_set_black(uncle); rb_set_black(parent); rb_set_red(gparent); node = gparent; continue; } } // Case 2条件:叔叔是黑色,且当前节点是右孩子 if (parent->right == node) { Node *tmp; rbtree_left_rotate(root, parent); tmp = parent; parent = node; node = tmp; } // Case 3条件:叔叔是黑色,且当前节点是左孩子。 rb_set_black(parent); rb_set_red(gparent); rbtree_right_rotate(root, gparent); } else//若“z的父节点”是“z的祖父节点的右孩子” { // Case 1条件:叔叔节点是红色 { Node *uncle = gparent->left; if (uncle && rb_is_red(uncle)) { rb_set_black(uncle); rb_set_black(parent); rb_set_red(gparent); node = gparent; continue; } } // Case 2条件:叔叔是黑色,且当前节点是左孩子 if (parent->left == node) { Node *tmp; rbtree_right_rotate(root, parent); tmp = parent; parent = node; node = tmp; } // Case 3条件:叔叔是黑色,且当前节点是右孩子。 rb_set_black(parent); rb_set_red(gparent); rbtree_left_rotate(root, gparent); } } // 将根节点设为黑色 rb_set_black(root->node);}
rbtree_insert_fixup(root, node)的作用是对应"上面所讲的第三步"。
5. 删除操作
将红黑树内的某一个节点删除。需要执行的操作依次是:首先,将红黑树当作一颗二叉查找树,将该节点从二叉查找树中删除;然后,通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。详细描述如下:
第一步:将红黑树当作一颗二叉查找树,将节点删除。
这和"删除常规二叉查找树中删除节点的方法是一样的"。分3种情况:
① 被删除节点没有儿子,即为叶节点。那么,直接将该节点删除就OK了。
② 被删除节点只有一个儿子。那么,直接删除该节点,并用该节点的唯一子节点顶替它的位置。
③ 被删除节点有两个儿子。那么,先找出它的后继节点;然后把“它的后继节点的内容”复制给“该节点的内容”;之后,删除“它的后继节点”。在这里,后继节点相当于替身,在将后继节点的内容复制给"被删除节点"之后,再将后继节点删除。这样就巧妙的将问题转换为"删除后继节点"的情况了,下面就考虑后继节点。 在"被删除节点"有两个非空子节点的情况下,它的后继节点不可能是双子非空。既然"的后继节点"不可能双子都非空,就意味着"该节点的后继节点"要么没有儿子,要么只有一个儿子。若没有儿子,则按"情况① "进行处理;若只有一个儿子,则按"情况② "进行处理。
第二步:通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。
因为"第一步"中删除节点之后,可能会违背红黑树的特性。所以需要通过"旋转和重新着色"来修正该树,使之重新成为一棵红黑树。
删除操作的实现代码(C语言)
/* * 删除结点 * * 参数说明: * tree 红黑树的根结点 * node 删除的结点 */void rbtree_delete(RBRoot *root, Node *node){ Node *child, *parent; int color; // 被删除节点的"左右孩子都不为空"的情况。 if ( (node->left!=NULL) && (node->right!=NULL) ) { // 被删节点的后继节点。(称为"取代节点") // 用它来取代"被删节点"的位置,然后再将"被删节点"去掉。 Node *replace = node; // 获取后继节点 replace = replace->right; while (replace->left != NULL) replace = replace->left; // "node节点"不是根节点(只有根节点不存在父节点) if (rb_parent(node)) { if (rb_parent(node)->left == node) rb_parent(node)->left = replace; else rb_parent(node)->right = replace; } else // "node节点"是根节点,更新根节点。 root->node = replace; // child是"取代节点"的右孩子,也是需要"调整的节点"。 // "取代节点"肯定不存在左孩子!因为它是一个后继节点。 child = replace->right; parent = rb_parent(replace); // 保存"取代节点"的颜色 color = rb_color(replace); // "被删除节点"是"它的后继节点的父节点" if (parent == node) { parent = replace; } else { // child不为空 if (child) rb_set_parent(child, parent); parent->left = child; replace->right = node->right; rb_set_parent(node->right, replace); } replace->parent = node->parent; replace->color = node->color; replace->left = node->left; node->left->parent = replace; if (color == BLACK) rbtree_delete_fixup(root, child, parent); free(node); return ; } if (node->left !=NULL) child = node->left; else child = node->right; parent = node->parent; // 保存"取代节点"的颜色 color = node->color; if (child) child->parent = parent; // "node节点"不是根节点 if (parent) { if (parent->left == node) parent->left = child; else parent->right = child; } else root->node = child; if (color == BLACK) rbtree_delete_fixup(root, child, parent); free(node);}
rbtree_delete(root, node)的作用是将"node"节点插入到红黑树中。其中,root是根,node是被插入节点。
删除修正操作的实现代码(C语言)
/* * 红黑树删除修正函数 * * 在从红黑树中删除插入节点之后(红黑树失去平衡),再调用该函数; * 目的是将它重新塑造成一颗红黑树。 * * 参数说明: * root 红黑树的根 * node 待修正的节点 */static void rbtree_delete_fixup(RBRoot *root, Node *node, Node *parent){ Node *other; while ((!node || rb_is_black(node)) && node != root->node) { if (parent->left == node) { other = parent->right; if (rb_is_red(other)) { // Case 1: x的兄弟w是红色的 rb_set_black(other); rb_set_red(parent); rbtree_left_rotate(root, parent); other = parent->right; } if ((!other->left || rb_is_black(other->left)) && (!other->right || rb_is_black(other->right))) { // Case 2: x的兄弟w是黑色,且w的俩个孩子也都是黑色的 rb_set_red(other); node = parent; parent = rb_parent(node); } else { if (!other->right || rb_is_black(other->right)) { // Case 3: x的兄弟w是黑色的,并且w的左孩子是红色,右孩子为黑色。 rb_set_black(other->left); rb_set_red(other); rbtree_right_rotate(root, other); other = parent->right; } // Case 4: x的兄弟w是黑色的;并且w的右孩子是红色的,左孩子任意颜色。 rb_set_color(other, rb_color(parent)); rb_set_black(parent); rb_set_black(other->right); rbtree_left_rotate(root, parent); node = root->node; break; } } else { other = parent->left; if (rb_is_red(other)) { // Case 1: x的兄弟w是红色的 rb_set_black(other); rb_set_red(parent); rbtree_right_rotate(root, parent); other = parent->left; } if ((!other->left || rb_is_black(other->left)) && (!other->right || rb_is_black(other->right))) { // Case 2: x的兄弟w是黑色,且w的俩个孩子也都是黑色的 rb_set_red(other); node = parent; parent = rb_parent(node); } else { if (!other->left || rb_is_black(other->left)) { // Case 3: x的兄弟w是黑色的,并且w的左孩子是红色,右孩子为黑色。 rb_set_black(other->right); rb_set_red(other); rbtree_left_rotate(root, other); other = parent->left; } // Case 4: x的兄弟w是黑色的;并且w的右孩子是红色的,左孩子任意颜色。 rb_set_color(other, rb_color(parent)); rb_set_black(parent); rb_set_black(other->left); rbtree_right_rotate(root, parent); node = root->node; break; } } } if (node) rb_set_black(node);}
rbtree_delete_fixup(root, node, parent)是对应"上面所讲的第三步"。
红黑树的C实现(完整源码)
下面是红黑数实现的完整代码和相应的测试程序。
(1) 除了上面所说的"左旋"、"右旋"、"添加"、"删除"等基本操作之后,还实现了"遍历"、"查找"、"打印"、"最小值"、"最大值"、"创建"、"销毁"等接口。
(2) 函数接口分为内部接口和外部接口。内部接口是static函数,外部接口则是非static函数,外部接口都在.h头文件中表明了。
(3) 测试代码中提供了"插入"和"删除"动作的检测开关。默认是关闭的,打开方法可以参考"代码中的说明"。建议在打开开关后,在草稿上自己动手绘制一下红黑树。
红黑树的实现文件(rbtree.h)
1 #ifndef _RED_BLACK_TREE_H_ 2 #define _RED_BLACK_TREE_H_ 3 4 #define RED 0 // 红色节点 5 #define BLACK 1 // 黑色节点 6 7 typedef int Type; 8 9 // 红黑树的节点10 typedef struct RBTreeNode{11 unsigned char color; // 颜色(RED 或 BLACK)12 Type key; // 关键字(键值)13 struct RBTreeNode *left; // 左孩子14 struct RBTreeNode *right; // 右孩子15 struct RBTreeNode *parent; // 父结点16 }Node, *RBTree;17 18 // 红黑树的根19 typedef struct rb_root{20 Node *node;21 }RBRoot;22 23 // 创建红黑树,返回"红黑树的根"!24 RBRoot* create_rbtree();25 26 // 销毁红黑树27 void destroy_rbtree(RBRoot *root);28 29 // 将结点插入到红黑树中。插入成功,返回0;失败返回-1。30 int insert_rbtree(RBRoot *root, Type key);31 32 // 删除结点(key为节点的值)33 void delete_rbtree(RBRoot *root, Type key);34 35 36 // 前序遍历"红黑树"37 void preorder_rbtree(RBRoot *root);38 // 中序遍历"红黑树"39 void inorder_rbtree(RBRoot *root);40 // 后序遍历"红黑树"41 void postorder_rbtree(RBRoot *root);42 43 // (递归实现)查找"红黑树"中键值为key的节点。找到的话,返回0;否则,返回-1。44 int rbtree_search(RBRoot *root, Type key);45 // (非递归实现)查找"红黑树"中键值为key的节点。找到的话,返回0;否则,返回-1。46 int iterative_rbtree_search(RBRoot *root, Type key);47 48 // 返回最小结点的值(将值保存到val中)。找到的话,返回0;否则返回-1。49 int rbtree_minimum(RBRoot *root, int *val);50 // 返回最大结点的值(将值保存到val中)。找到的话,返回0;否则返回-1。51 int rbtree_maximum(RBRoot *root, int *val);52 53 // 打印红黑树54 void print_rbtree(RBRoot *root);55 56 #endif
红黑树的实现文件(rbtree.c)
1 /** 2 * C语言实现的红黑树(Red Black Tree) 3 * 4 * @author skywang 5 * @date 2013/11/18 6 */ 7 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include "rbtree.h" 11 12 #define rb_parent(r) ((r)->parent) 13 #define rb_color(r) ((r)->color) 14 #define rb_is_red(r) ((r)->color==RED) 15 #define rb_is_black(r) ((r)->color==BLACK) 16 #define rb_set_black(r) do { (r)->color = BLACK; } while (0) 17 #define rb_set_red(r) do { (r)->color = RED; } while (0) 18 #define rb_set_parent(r,p) do { (r)->parent = (p); } while (0) 19 #define rb_set_color(r,c) do { (r)->color = (c); } while (0) 20 21 /* 22 * 创建红黑树,返回"红黑树的根"! 23 */ 24 RBRoot* create_rbtree() 25 { 26 RBRoot *root = (RBRoot *)malloc(sizeof(RBRoot)); 27 root->node = NULL; 28 29 return root; 30 } 31 32 /* 33 * 前序遍历"红黑树" 34 */ 35 static void preorder(RBTree tree) 36 { 37 if(tree != NULL) 38 { 39 printf("%d ", tree->key); 40 preorder(tree->left); 41 preorder(tree->right); 42 } 43 } 44 void preorder_rbtree(RBRoot *root) 45 { 46 if (root) 47 preorder(root->node); 48 } 49 50 /* 51 * 中序遍历"红黑树" 52 */ 53 static void inorder(RBTree tree) 54 { 55 if(tree != NULL) 56 { 57 inorder(tree->left); 58 printf("%d ", tree->key); 59 inorder(tree->right); 60 } 61 } 62 63 void inorder_rbtree(RBRoot *root) 64 { 65 if (root) 66 inorder(root->node); 67 } 68 69 /* 70 * 后序遍历"红黑树" 71 */ 72 static void postorder(RBTree tree) 73 { 74 if(tree != NULL) 75 { 76 postorder(tree->left); 77 postorder(tree->right); 78 printf("%d ", tree->key); 79 } 80 } 81 82 void postorder_rbtree(RBRoot *root) 83 { 84 if (root) 85 postorder(root->node); 86 } 87 88 /* 89 * (递归实现)查找"红黑树x"中键值为key的节点 90 */ 91 static Node* search(RBTree x, Type key) 92 { 93 if (x==NULL || x->key==key) 94 return x; 95 96 if (key < x->key) 97 return search(x->left, key); 98 else 99 return search(x->right, key);100 }101 int rbtree_search(RBRoot *root, Type key)102 {103 if (root)104 return search(root->node, key)? 0 : -1;105 }106 107 /*108 * (非递归实现)查找"红黑树x"中键值为key的节点109 */110 static Node* iterative_search(RBTree x, Type key)111 {112 while ((x!=NULL) && (x->key!=key))113 {114 if (key < x->key)115 x = x->left;116 else117 x = x->right;118 }119 120 return x;121 }122 int iterative_rbtree_search(RBRoot *root, Type key)123 {124 if (root)125 return iterative_search(root->node, key) ? 0 : -1;126 }127 128 /* 129 * 查找最小结点:返回tree为根结点的红黑树的最小结点。130 */131 static Node* minimum(RBTree tree)132 {133 if (tree == NULL)134 return NULL;135 136 while(tree->left != NULL)137 tree = tree->left;138 return tree;139 }140 141 int rbtree_minimum(RBRoot *root, int *val)142 {143 Node *node;144 145 if (root)146 node = minimum(root->node);147 148 if (node == NULL)149 return -1;150 151 *val = node->key;152 return 0;153 }154 155 /* 156 * 查找最大结点:返回tree为根结点的红黑树的最大结点。157 */158 static Node* maximum(RBTree tree)159 {160 if (tree == NULL)161 return NULL;162 163 while(tree->right != NULL)164 tree = tree->right;165 return tree;166 }167 168 int rbtree_maximum(RBRoot *root, int *val)169 {170 Node *node;171 172 if (root)173 node = maximum(root->node);174 175 if (node == NULL)176 return -1;177 178 *val = node->key;179 return 0;180 }181 182 /* 183 * 找结点(x)的后继结点。即,查找"红黑树中数据值大于该结点"的"最小结点"。184 */185 static Node* rbtree_successor(RBTree x)186 {187 // 如果x存在右孩子,则"x的后继结点"为 "以其右孩子为根的子树的最小结点"。188 if (x->right != NULL)189 return minimum(x->right);190 191 // 如果x没有右孩子。则x有以下两种可能:192 // (01) x是"一个左孩子",则"x的后继结点"为 "它的父结点"。193 // (02) x是"一个右孩子",则查找"x的最低的父结点,并且该父结点要具有左孩子",找到的这个"最低的父结点"就是"x的后继结点"。194 Node* y = x->parent;195 while ((y!=NULL) && (x==y->right))196 {197 x = y;198 y = y->parent;199 }200 201 return y;202 }203 204 /* 205 * 找结点(x)的前驱结点。即,查找"红黑树中数据值小于该结点"的"最大结点"。206 */207 static Node* rbtree_predecessor(RBTree x)208 {209 // 如果x存在左孩子,则"x的前驱结点"为 "以其左孩子为根的子树的最大结点"。210 if (x->left != NULL)211 return maximum(x->left);212 213 // 如果x没有左孩子。则x有以下两种可能:214 // (01) x是"一个右孩子",则"x的前驱结点"为 "它的父结点"。215 // (01) x是"一个左孩子",则查找"x的最低的父结点,并且该父结点要具有右孩子",找到的这个"最低的父结点"就是"x的前驱结点"。216 Node* y = x->parent;217 while ((y!=NULL) && (x==y->left))218 {219 x = y;220 y = y->parent;221 }222 223 return y;224 }225 226 /* 227 * 对红黑树的节点(x)进行左旋转228 *229 * 左旋示意图(对节点x进行左旋):230 * px px231 * / /232 * x y 233 * / \ --(左旋)--> / \ #234 * lx y x ry 235 * / \ / \236 * ly ry lx ly 237 *238 *239 */240 static void rbtree_left_rotate(RBRoot *root, Node *x)241 {242 // 设置x的右孩子为y243 Node *y = x->right;244 245 // 将 “y的左孩子” 设为 “x的右孩子”;246 // 如果y的左孩子非空,将 “x” 设为 “y的左孩子的父亲”247 x->right = y->left;248 if (y->left != NULL)249 y->left->parent = x;250 251 // 将 “x的父亲” 设为 “y的父亲”252 y->parent = x->parent;253 254 if (x->parent == NULL)255 {256 //tree = y; // 如果 “x的父亲” 是空节点,则将y设为根节点257 root->node = y; // 如果 “x的父亲” 是空节点,则将y设为根节点258 }259 else260 {261 if (x->parent->left == x)262 x->parent->left = y; // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”263 else264 x->parent->right = y; // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”265 }266 267 // 将 “x” 设为 “y的左孩子”268 y->left = x;269 // 将 “x的父节点” 设为 “y”270 x->parent = y;271 }272 273 /* 274 * 对红黑树的节点(y)进行右旋转275 *276 * 右旋示意图(对节点y进行左旋):277 * py py278 * / /279 * y x 280 * / \ --(右旋)--> / \ #281 * x ry lx y 282 * / \ / \ #283 * lx rx rx ry284 * 285 */286 static void rbtree_right_rotate(RBRoot *root, Node *y)287 {288 // 设置x是当前节点的左孩子。289 Node *x = y->left;290 291 // 将 “x的右孩子” 设为 “y的左孩子”;292 // 如果"x的右孩子"不为空的话,将 “y” 设为 “x的右孩子的父亲”293 y->left = x->right;294 if (x->right != NULL)295 x->right->parent = y;296 297 // 将 “y的父亲” 设为 “x的父亲”298 x->parent = y->parent;299 300 if (y->parent == NULL) 301 {302 //tree = x; // 如果 “y的父亲” 是空节点,则将x设为根节点303 root->node = x; // 如果 “y的父亲” 是空节点,则将x设为根节点304 }305 else306 {307 if (y == y->parent->right)308 y->parent->right = x; // 如果 y是它父节点的右孩子,则将x设为“y的父节点的右孩子”309 else310 y->parent->left = x; // (y是它父节点的左孩子) 将x设为“x的父节点的左孩子”311 }312 313 // 将 “y” 设为 “x的右孩子”314 x->right = y;315 316 // 将 “y的父节点” 设为 “x”317 y->parent = x;318 }319 320 /*321 * 红黑树插入修正函数322 *323 * 在向红黑树中插入节点之后(失去平衡),再调用该函数;324 * 目的是将它重新塑造成一颗红黑树。325 *326 * 参数说明:327 * root 红黑树的根328 * node 插入的结点 // 对应《算法导论》中的z329 */330 static void rbtree_insert_fixup(RBRoot *root, Node *node)331 {332 Node *parent, *gparent;333 334 // 若“父节点存在,并且父节点的颜色是红色”335 while ((parent = rb_parent(node)) && rb_is_red(parent))336 {337 gparent = rb_parent(parent);338 339 //若“父节点”是“祖父节点的左孩子”340 if (parent == gparent->left)341 {342 // Case 1条件:叔叔节点是红色343 {344 Node *uncle = gparent->right;345 if (uncle && rb_is_red(uncle))346 {347 rb_set_black(uncle);348 rb_set_black(parent);349 rb_set_red(gparent);350 node = gparent;351 continue;352 }353 }354 355 // Case 2条件:叔叔是黑色,且当前节点是右孩子356 if (parent->right == node)357 {358 Node *tmp;359 rbtree_left_rotate(root, parent);360 tmp = parent;361 parent = node;362 node = tmp;363 }364 365 // Case 3条件:叔叔是黑色,且当前节点是左孩子。366 rb_set_black(parent);367 rb_set_red(gparent);368 rbtree_right_rotate(root, gparent);369 } 370 else//若“z的父节点”是“z的祖父节点的右孩子”371 {372 // Case 1条件:叔叔节点是红色373 {374 Node *uncle = gparent->left;375 if (uncle && rb_is_red(uncle))376 {377 rb_set_black(uncle);378 rb_set_black(parent);379 rb_set_red(gparent);380 node = gparent;381 continue;382 }383 }384 385 // Case 2条件:叔叔是黑色,且当前节点是左孩子386 if (parent->left == node)387 {388 Node *tmp;389 rbtree_right_rotate(root, parent);390 tmp = parent;391 parent = node;392 node = tmp;393 }394 395 // Case 3条件:叔叔是黑色,且当前节点是右孩子。396 rb_set_black(parent);397 rb_set_red(gparent);398 rbtree_left_rotate(root, gparent);399 }400 }401 402 // 将根节点设为黑色403 rb_set_black(root->node);404 }405 406 /*407 * 添加节点:将节点(node)插入到红黑树中408 *409 * 参数说明:410 * root 红黑树的根411 * node 插入的结点 // 对应《算法导论》中的z412 */413 static void rbtree_insert(RBRoot *root, Node *node)414 {415 Node *y = NULL;416 Node *x = root->node;417 418 // 1. 将红黑树当作一颗二叉查找树,将节点添加到二叉查找树中。419 while (x != NULL)420 {421 y = x;422 if (node->key < x->key)423 x = x->left;424 else425 x = x->right;426 }427 rb_parent(node) = y;428 429 if (y != NULL)430 {431 if (node->key < y->key)432 y->left = node; // 情况2:若“node所包含的值” < “y所包含的值”,则将node设为“y的左孩子”433 else434 y->right = node; // 情况3:(“node所包含的值” >= “y所包含的值”)将node设为“y的右孩子” 435 }436 else437 {438 root->node = node; // 情况1:若y是空节点,则将node设为根439 }440 441 // 2. 设置节点的颜色为红色442 node->color = RED;443 444 // 3. 将它重新修正为一颗二叉查找树445 rbtree_insert_fixup(root, node);446 }447 448 /*449 * 创建结点450 *451 * 参数说明:452 * key 是键值。453 * parent 是父结点。454 * left 是左孩子。455 * right 是右孩子。456 */457 static Node* create_rbtree_node(Type key, Node *parent, Node *left, Node* right)458 {459 Node* p;460 461 if ((p = (Node *)malloc(sizeof(Node))) == NULL)462 return NULL;463 p->key = key;464 p->left = left;465 p->right = right;466 p->parent = parent;467 p->color = BLACK; // 默认为黑色468 469 return p;470 }471 472 /* 473 * 新建结点(节点键值为key),并将其插入到红黑树中474 *475 * 参数说明:476 * root 红黑树的根477 * key 插入结点的键值478 * 返回值:479 * 0,插入成功480 * -1,插入失败481 */482 int insert_rbtree(RBRoot *root, Type key)483 {484 Node *node; // 新建结点485 486 // 不允许插入相同键值的节点。487 // (若想允许插入相同键值的节点,注释掉下面两句话即可!)488 if (search(root->node, key) != NULL)489 return -1;490 491 // 如果新建结点失败,则返回。492 if ((node=create_rbtree_node(key, NULL, NULL, NULL)) == NULL)493 return -1;494 495 rbtree_insert(root, node);496 497 return 0;498 }499 500 /*501 * 红黑树删除修正函数502 *503 * 在从红黑树中删除插入节点之后(红黑树失去平衡),再调用该函数;504 * 目的是将它重新塑造成一颗红黑树。505 *506 * 参数说明:507 * root 红黑树的根508 * node 待修正的节点509 */510 static void rbtree_delete_fixup(RBRoot *root, Node *node, Node *parent)511 {512 Node *other;513 514 while ((!node || rb_is_black(node)) && node != root->node)515 {516 if (parent->left == node)517 {518 other = parent->right;519 if (rb_is_red(other))520 {521 // Case 1: x的兄弟w是红色的 522 rb_set_black(other);523 rb_set_red(parent);524 rbtree_left_rotate(root, parent);525 other = parent->right;526 }527 if ((!other->left || rb_is_black(other->left)) &&528 (!other->right || rb_is_black(other->right)))529 {530 // Case 2: x的兄弟w是黑色,且w的俩个孩子也都是黑色的 531 rb_set_red(other);532 node = parent;533 parent = rb_parent(node);534 }535 else536 {537 if (!other->right || rb_is_black(other->right))538 {539 // Case 3: x的兄弟w是黑色的,并且w的左孩子是红色,右孩子为黑色。 540 rb_set_black(other->left);541 rb_set_red(other);542 rbtree_right_rotate(root, other);543 other = parent->right;544 }545 // Case 4: x的兄弟w是黑色的;并且w的右孩子是红色的,左孩子任意颜色。546 rb_set_color(other, rb_color(parent));547 rb_set_black(parent);548 rb_set_black(other->right);549 rbtree_left_rotate(root, parent);550 node = root->node;551 break;552 }553 }554 else555 {556 other = parent->left;557 if (rb_is_red(other))558 {559 // Case 1: x的兄弟w是红色的 560 rb_set_black(other);561 rb_set_red(parent);562 rbtree_right_rotate(root, parent);563 other = parent->left;564 }565 if ((!other->left || rb_is_black(other->left)) &&566 (!other->right || rb_is_black(other->right)))567 {568 // Case 2: x的兄弟w是黑色,且w的俩个孩子也都是黑色的 569 rb_set_red(other);570 node = parent;571 parent = rb_parent(node);572 }573 else574 {575 if (!other->left || rb_is_black(other->left))576 {577 // Case 3: x的兄弟w是黑色的,并且w的左孩子是红色,右孩子为黑色。 578 rb_set_black(other->right);579 rb_set_red(other);580 rbtree_left_rotate(root, other);581 other = parent->left;582 }583 // Case 4: x的兄弟w是黑色的;并且w的右孩子是红色的,左孩子任意颜色。584 rb_set_color(other, rb_color(parent));585 rb_set_black(parent);586 rb_set_black(other->left);587 rbtree_right_rotate(root, parent);588 node = root->node;589 break;590 }591 }592 }593 if (node)594 rb_set_black(node);595 }596 597 /* 598 * 删除结点599 *600 * 参数说明:601 * tree 红黑树的根结点602 * node 删除的结点603 */604 void rbtree_delete(RBRoot *root, Node *node)605 {606 Node *child, *parent;607 int color;608 609 // 被删除节点的"左右孩子都不为空"的情况。610 if ( (node->left!=NULL) && (node->right!=NULL) ) 611 {612 // 被删节点的后继节点。(称为"取代节点")613 // 用它来取代"被删节点"的位置,然后再将"被删节点"去掉。614 Node *replace = node;615 616 // 获取后继节点617 replace = replace->right;618 while (replace->left != NULL)619 replace = replace->left;620 621 // "node节点"不是根节点(只有根节点不存在父节点)622 if (rb_parent(node))623 {624 if (rb_parent(node)->left == node)625 rb_parent(node)->left = replace;626 else627 rb_parent(node)->right = replace;628 } 629 else 630 // "node节点"是根节点,更新根节点。631 root->node = replace;632 633 // child是"取代节点"的右孩子,也是需要"调整的节点"。634 // "取代节点"肯定不存在左孩子!因为它是一个后继节点。635 child = replace->right;636 parent = rb_parent(replace);637 // 保存"取代节点"的颜色638 color = rb_color(replace);639 640 // "被删除节点"是"它的后继节点的父节点"641 if (parent == node)642 {643 parent = replace;644 } 645 else646 {647 // child不为空648 if (child)649 rb_set_parent(child, parent);650 parent->left = child;651 652 replace->right = node->right;653 rb_set_parent(node->right, replace);654 }655 656 replace->parent = node->parent;657 replace->color = node->color;658 replace->left = node->left;659 node->left->parent = replace;660 661 if (color == BLACK)662 rbtree_delete_fixup(root, child, parent);663 free(node);664 665 return ;666 }667 668 if (node->left !=NULL)669 child = node->left;670 else 671 child = node->right;672 673 parent = node->parent;674 // 保存"取代节点"的颜色675 color = node->color;676 677 if (child)678 child->parent = parent;679 680 // "node节点"不是根节点681 if (parent)682 {683 if (parent->left == node)684 parent->left = child;685 else686 parent->right = child;687 }688 else689 root->node = child;690 691 if (color == BLACK)692 rbtree_delete_fixup(root, child, parent);693 free(node);694 }695 696 /* 697 * 删除键值为key的结点698 *699 * 参数说明:700 * tree 红黑树的根结点701 * key 键值702 */703 void delete_rbtree(RBRoot *root, Type key)704 {705 Node *z, *node; 706 707 if ((z = search(root->node, key)) != NULL)708 rbtree_delete(root, z);709 }710 711 /*712 * 销毁红黑树713 */714 static void rbtree_destroy(RBTree tree)715 {716 if (tree==NULL)717 return ;718 719 if (tree->left != NULL)720 rbtree_destroy(tree->left);721 if (tree->right != NULL)722 rbtree_destroy(tree->right);723 724 free(tree);725 }726 727 void destroy_rbtree(RBRoot *root)728 {729 if (root != NULL)730 rbtree_destroy(root->node);731 732 free(root);733 }734 735 /*736 * 打印"红黑树"737 *738 * tree -- 红黑树的节点739 * key -- 节点的键值 740 * direction -- 0,表示该节点是根节点;741 * -1,表示该节点是它的父结点的左孩子;742 * 1,表示该节点是它的父结点的右孩子。743 */744 static void rbtree_print(RBTree tree, Type key, int direction)745 {746 if(tree != NULL)747 {748 if(direction==0) // tree是根节点749 printf("%2d(B) is root\n", tree->key);750 else // tree是分支节点751 printf("%2d(%s) is %2d's %6s child\n", tree->key, rb_is_red(tree)?"R":"B", key, direction==1?"right" : "left");752 753 rbtree_print(tree->left, tree->key, -1);754 rbtree_print(tree->right,tree->key, 1);755 }756 }757 758 void print_rbtree(RBRoot *root)759 {760 if (root!=NULL && root->node!=NULL)761 rbtree_print(root->node, root->node->key, 0);762 }
红黑树的测试文件(rbtree_test.c)
1 /** 2 * C语言实现的红黑树(Red Black Tree) 3 * 4 * @author skywang 5 * @date 2013/11/18 6 */ 7 8 #include <stdio.h> 9 #include "rbtree.h"10 11 #define CHECK_INSERT 0 // "插入"动作的检测开关(0,关闭;1,打开)12 #define CHECK_DELETE 0 // "删除"动作的检测开关(0,关闭;1,打开)13 #define LENGTH(a) ( (sizeof(a)) / (sizeof(a[0])) )14 15 void main()16 {17 int a[] = {10, 40, 30, 60, 90, 70, 20, 50, 80};18 int i, ilen=LENGTH(a);19 RBRoot *root=NULL;20 21 root = create_rbtree();22 printf("== 原始数据: ");23 for(i=0; i<ilen; i++)24 printf("%d ", a[i]);25 printf("\n");26 27 for(i=0; i<ilen; i++)28 {29 insert_rbtree(root, a[i]);30 #if CHECK_INSERT31 printf("== 添加节点: %d\n", a[i]);32 printf("== 树的详细信息: \n");33 print_rbtree(root);34 printf("\n");35 #endif36 }37 38 printf("== 前序遍历: ");39 preorder_rbtree(root);40 41 printf("\n== 中序遍历: ");42 inorder_rbtree(root);43 44 printf("\n== 后序遍历: ");45 postorder_rbtree(root);46 printf("\n");47 48 if (rbtree_minimum(root, &i)==0)49 printf("== 最小值: %d\n", i);50 if (rbtree_maximum(root, &i)==0)51 printf("== 最大值: %d\n", i);52 printf("== 树的详细信息: \n");53 print_rbtree(root);54 printf("\n");55 56 #if CHECK_DELETE57 for(i=0; i<ilen; i++)58 {59 delete_rbtree(root, a[i]);60 61 printf("== 删除节点: %d\n", a[i]);62 if (root)63 {64 printf("== 树的详细信息: \n");65 print_rbtree(root);66 printf("\n");67 }68 }69 #endif70 71 destroy_rbtree(root);72 }
红黑树的C测试程序
前面已经给出了红黑树的测试程序(rbtree_test.c),这里就不再重复说明。下面是测试程序的运行结果:
== 原始数据: 10 40 30 60 90 70 20 50 80 == 前序遍历: 30 10 20 60 40 50 80 70 90 == 中序遍历: 10 20 30 40 50 60 70 80 90 == 后序遍历: 20 10 50 40 70 90 80 60 30 == 最小值: 10== 最大值: 90== 树的详细信息: 30(B) is root10(B) is 30's left child20(R) is 10's right child60(R) is 30's right child40(B) is 60's left child50(R) is 40's right child80(B) is 60's right child70(R) is 80's left child90(R) is 80's right child
- 红黑树(二)之 C语言的实现
- 红黑树(二)之 C语言的实现
- 红黑树之 C语言的实现
- 遗传算法的C语言实现(二)
- C语言的那些事儿之二
- 从零开始的C语言之路<二>
- 数据结构(严蔚敏)之二——链表的c语言实现
- 单链表的基本操作大全之C语言实现(二)
- 排序之二:希尔排序(C语言实现)
- 【C语言】实现一个通讯录(二:动态的实现)
- 二叉排序树C语言实现二
- 数据结构之队列的实现(c语言)
- 数据结构之栈的c语言实现
- C语言实现的数据结构之------哈希表
- c语言之链表的实现
- 数据结构之单链表的C语言实现
- 数据结构之双链表的C语言实现
- 堆栈的简单实现之二:进制转换方面的简单应用(C语言实现)
- 队列的图文解析 和 对应3种语言的实现(C/C++/Java)
- 二叉查找树(一)之 图文解析 和 C语言的实现
- AVL树(一)之 图文解析 和 C语言的实现
- 伸展树(一)之 图文解析 和 C语言的实现
- 红黑树(一)之 原理和算法详细介绍
- 红黑树(二)之 C语言的实现
- 红黑树(三)之 Linux内核中红黑树的经典实现
- LeetCode 148. Sort List
- 哈夫曼树(一)之 C语言详解
- JAVA 里的移位操作符浅析+凝问
- 二叉堆(一)之 图文解析 和 C语言的实现
- 左倾堆(一)之 图文解析 和 C语言的实现
- 斜堆(一)之 C语言的实现
- 二项堆(一)之 图文解析 和 C语言的实现