标题我是引用作者的 :)
红黑树是一种平衡的二叉搜索树,它的插入操作和普通的二叉搜索树差不多,只不过多了些平衡的代码。红黑树的具体定义和性质什么的,看原文就可以了,作者讲的很清楚。当然也可以网上搜下,一大把。
既然是”二叉搜索树”,那么它的前驱后继、最大最小、三种遍历等这些操作都是一样的,代码可以参照二叉搜索树这节,这里就不贴出来了。这节主要是解释下我学习后自己实现的插入操作的代码,当然,删除操作的代码也应该讲下的,因为删除操作和普通二叉搜索树的也不同,不过删除操作的代码暂未实现,等实现了再加上。
贴代码之前,我先把原文中作者讲的比较关键的地方贴出来,插入操作根据关键的内容就可以实现了,很简单。
修复代码就是将这四种情况描述清楚就行了,很简单吧,所以看懂这图很重要。截图来自原文。
除了上面那张图,看下下面这张图也很有用,因为插入操作需要实现的几个函数和这差不多。截图同样来自原文。
图中几个等式的定义的意思就是:要实现某个函数的功能,嵌套另一个函数的功能来实现就行了,可能还会分几种情况,整体就是这样。然后还有作者提到的模式匹配,这个理解为结合率就差不多了。我简单解释下balance(T)
的意思:如果树的结构满足match(T)
这种情况,我们就把它修复为(R, (B, A, x, B), y, (B, C, z, D))
这种结构。这个结构是什么意思呢?就是节点y的左右两个子节点分别是x、z,且节点y的颜色是红色R,然后节点x的左右两个子节点分别是A、B,且节点x的颜色为黑色B,然后节点y的左右两个子节点分别是C、D,且节点y的颜色为黑色B(是不是和结合律差不多呢),如果是其它情况otherwise,就不用修复了,直接是原来的T就行了。其它的等式也这样理解就行了。
下面正式贴代码,我感觉代码有点啰嗦,不知是我实现的啰嗦还是本来就这么啰嗦…肯定有更好地实现代码,我的仅供参考。C语言并没有模式匹配这个功能,所以用模式匹配是不可能的了,只能老老实实写代码进行判断这样子。
红黑树头文件:
#ifndef _RBTREE_H_
#define _RBTREE_H_
// 节点还是只能存unsigned long类型的变量
typedef struct _wtlRBT_NODE wtlRBT_NODE;
typedef struct _wtlRBTREE {
wtlRBT_NODE* root;
int height;
} wtlRBTREE;
wtlRBTREE* wtlRBTree_Create(void);
void wtlRBTree_Destroy(wtlRBTREE** tree);
void wtlRBTree_Insert(
wtlRBTREE* tree, unsigned long value
);
void wtlRBTree_PreOrder(wtlRBTREE* tree);
void wtlRBTree_InOrder(wtlRBTREE* tree);
void wtlRBTree_PostOrder(wtlRBTREE* tree);
wtlRBT_NODE* wtlRBTree_LookUp(
wtlRBTREE* tree, unsigned long value
);
wtlRBT_NODE* wtlRBTree_Min(wtlRBTREE* tree);
wtlRBT_NODE* wtlRBTree_Max(wtlRBTREE* tree);
wtlRBT_NODE* wtlRBTree_Precursor(
wtlRBTREE* tree, unsigned long value
);
wtlRBT_NODE* wtlRBTree_Successor(
wtlRBTREE* tree, unsigned long value
);
/* Functions of node's options */
unsigned long wtlRBTNode_Value(wtlRBT_NODE* node);
wtlRBT_NODE* wtlRBTNode_Left(wtlRBT_NODE* node);
wtlRBT_NODE* wtlRBTNode_Right(wtlRBT_NODE* node);
#endif /* _RBTREE_H_ */
红黑树实现文件:
typedef enum {
eRED,
eBLACK
} wtlCOLOR;
typedef struct _wtlRBT_NODE{
struct _wtlRBT_NODE* parent;
struct _wtlRBT_NODE* left;
struct _wtlRBT_NODE* right;
wtlCOLOR color;
unsigned long value;
} wtlRBT_NODE;
// 其它函数就不贴出来了,不然太长了
......
// 需要修复的情况1,对应截图中的一种。
// 这函数没什么好讲的,就是代码写的那样,-1表示符合该情况,0表示不符合
int wtlRBTree_CaseA(wtlRBT_NODE* x){
// 如果给定的节点x为空,或它的颜色为黑色,直接就不符合情况了
if (NULL == x || wtlRBTNode_Color(x) == eBLACK) {
return -1;
}
// 否则继续判断父节点y的情况
wtlRBT_NODE* y = wtlRBTNode_Parent(x);
if (NULL == y || wtlRBTNode_Color(y) == eBLACK) {
return -1;
}
// 再判断y的父节点z的情况
wtlRBT_NODE* z = wtlRBTNode_Parent(y);
if (x == wtlRBTNode_Left(y) && y == wtlRBTNode_Left(z)) {
return 0;
}
return -1;
}
// 情况1的修复函数。tree就是整棵红黑树,x就是当前插入,且符合情况1的节点
// 注意该函数的调用需要先调用wtlRBTree_CaseA()进行判断,直接调用可能出错的
wtlRBTREE* wtlRBTree_BalanceA(wtlRBTREE* tree, wtlRBT_NODE* x){
printf("BalanceA\n");
// 先获取截图中所涉及到的节点
wtlRBT_NODE* y = wtlRBTNode_Parent(x);
wtlRBT_NODE* z = wtlRBTNode_Parent(y);
wtlRBT_NODE* r = wtlRBTNode_Parent(z);
wtlRBT_NODE* c = wtlRBTNode_Right(y);
// 直接设置修复后各节点之间的关系,就是截图中中间那种结构。这几个函数没贴出来,不过都是很简单的
wtlRBTNode_SetRight(y, z);
wtlRBTNode_SetLeft(z, c);
wtlRBTNode_SetColor(y, eRED);
wtlRBTNode_SetColor(x, eBLACK);
wtlRBTNode_SetColor(z, eBLACK);
// 这几个节点并不是整棵树,所以需要判断一下
// 如果节点r为空
if (NULL == r) {
// 设置节点y的父节点
wtlRBTNode_SetParent(y, NULL);
// 此时整棵树的根节点就是y了
tree->root = y;
// 然后返回整棵树
return tree;
}
// 如果原先的结构(未修复之前),节点z为节点r的左节点
if (z == wtlRBTNode_Left(r)) {
// 设置节点y为节点r的左节点(修复后)
wtlRBTNode_SetLeft(r, y);
} else {
// 反之设置节点y为节点r的右节点(修复后)
wtlRBTNode_SetRight(r, y);
}
// 返回整棵树
return tree;
}
// 剩下3中情况和修复函数就懒得讲了,理解情况1,在结合截图来就行了
int wtlRBTree_CaseB(wtlRBT_NODE* z){
if (NULL == z || wtlRBTNode_Color(z) == eBLACK) {
return -1;
}
wtlRBT_NODE* y = wtlRBTNode_Parent(z);
if (NULL == y || wtlRBTNode_Color(y) == eBLACK) {
return -1;
}
wtlRBT_NODE* x = wtlRBTNode_Parent(y);
if (z == wtlRBTNode_Right(y) && y == wtlRBTNode_Right(x)) {
return 0;
}
return -1;
}
wtlRBTREE* wtlRBTree_BalanceB(wtlRBTREE* tree, wtlRBT_NODE* z){
printf("BalanceB\n");
wtlRBT_NODE* y = wtlRBTNode_Parent(z);
wtlRBT_NODE* x = wtlRBTNode_Parent(y);
wtlRBT_NODE* b = wtlRBTNode_Left(y);
wtlRBT_NODE* r = wtlRBTNode_Parent(x);
wtlRBTNode_SetLeft(y, x);
wtlRBTNode_SetRight(x, b);
wtlRBTNode_SetColor(y, eRED);
wtlRBTNode_SetColor(x, eBLACK);
wtlRBTNode_SetColor(z, eBLACK);
if (NULL == r) {
wtlRBTNode_SetParent(y, NULL);
tree->root = y;
return tree;
}
if (x == wtlRBTNode_Left(r)) {
wtlRBTNode_SetLeft(r, y);
} else {
wtlRBTNode_SetRight(r, y);
}
return tree;
}
int wtlRBTree_CaseC(wtlRBT_NODE* y){
if (NULL == y || wtlRBTNode_Color(y) == eBLACK) {
return -1;
}
wtlRBT_NODE* x = wtlRBTNode_Parent(y);
if (NULL == x || wtlRBTNode_Color(x) == eBLACK) {
return -1;
}
wtlRBT_NODE* z = wtlRBTNode_Parent(x);
if (y == wtlRBTNode_Right(x) && x == wtlRBTNode_Left(z)) {
return 0;
}
return -1;
}
wtlRBTREE* wtlRBTree_BalanceC(wtlRBTREE* tree, wtlRBT_NODE* y){
printf("BalanceC\n");
wtlRBT_NODE* x = wtlRBTNode_Parent(y);
wtlRBT_NODE* z = wtlRBTNode_Parent(x);
wtlRBT_NODE* b = wtlRBTNode_Left(y);
wtlRBT_NODE* c = wtlRBTNode_Right(y);
wtlRBT_NODE* r = wtlRBTNode_Parent(z);
wtlRBTNode_SetChildren(y, x, z);
wtlRBTNode_SetRight(x, b);
wtlRBTNode_SetLeft(z, c);
wtlRBTNode_SetColor(y, eRED);
wtlRBTNode_SetColor(x, eBLACK);
wtlRBTNode_SetColor(z, eBLACK);
if (NULL == r) {
wtlRBTNode_SetParent(y, NULL);
tree->root = y;
return tree;
}
if (z == wtlRBTNode_Left(r)) {
wtlRBTNode_SetLeft(r, y);
} else {
wtlRBTNode_SetRight(r, y);
}
return tree;
}
int wtlRBTree_CaseD(wtlRBT_NODE* y){
if (NULL == y || wtlRBTNode_Color(y) == eBLACK) {
return -1;
}
wtlRBT_NODE* z = wtlRBTNode_Parent(y);
if (NULL == z || wtlRBTNode_Color(z) == eBLACK) {
return -1;
}
wtlRBT_NODE* x = wtlRBTNode_Parent(z);
if (y == wtlRBTNode_Left(z) && z == wtlRBTNode_Right(x)) {
return 0;
}
return -1;
}
wtlRBTREE* wtlRBTree_BalanceD(wtlRBTREE* tree, wtlRBT_NODE* y){
printf("BalanceD\n");
wtlRBT_NODE* z = wtlRBTNode_Parent(y);
wtlRBT_NODE* x = wtlRBTNode_Parent(z);
wtlRBT_NODE* b = wtlRBTNode_Left(y);
wtlRBT_NODE* c = wtlRBTNode_Right(y);
wtlRBT_NODE* r = wtlRBTNode_Parent(x);
wtlRBTNode_SetChildren(y, x, z);
wtlRBTNode_SetRight(x, b);
wtlRBTNode_SetLeft(z, c);
wtlRBTNode_SetColor(y, eRED);
wtlRBTNode_SetColor(x, eBLACK);
wtlRBTNode_SetColor(z, eBLACK);
if (NULL == r) {
wtlRBTNode_SetParent(y, NULL);
tree->root = y;
return tree;
}
if (x == wtlRBTNode_Left(r)) {
wtlRBTNode_SetLeft(r, y);
} else {
wtlRBTNode_SetRight(r, y);
}
return tree;
}
// 统一修复的函数。tree为整棵树,node为任意节点
wtlRBTREE* wtlRBTree_Balance(wtlRBTREE* tree, wtlRBT_NODE* node){
// 如果都不符合那四种情况,就不需要修复了,直接返回整棵树
if (-1 == wtlRBTree_CaseA(node) &&
-1 == wtlRBTree_CaseB(node) &&
-1 == wtlRBTree_CaseC(node) &&
-1 == wtlRBTree_CaseD(node)
) {
return tree;
}
// 符合情况1,修复情况1
if (wtlRBTree_CaseA(node) == 0) {
printf("caseA:%ld\n", node->value);
wtlRBTree_BalanceA(tree, node);
}
if (wtlRBTree_CaseB(node) == 0) {
printf("caseB:%ld\n", node->value);
wtlRBTree_BalanceB(tree, node);
}
if (wtlRBTree_CaseC(node) == 0) {
printf("caseC:%ld\n", node->value);
wtlRBTree_BalanceC(tree, node);
}
if (wtlRBTree_CaseD(node) == 0) {
printf("caseD:%ld\n", node->value);
wtlRBTree_BalanceD(tree, node);
}
// 修复操作并不是进行一次就行了的,需要递归地修复,直到不存在连续的两个红色节点
// 如果当前需要修复的节点node的颜色为红色(注意,node可能为父节点,也可能为子节点,
// 当它为父节点的时候,会被上面的修复操作设置为红色,为子节点则设置为黑色)
if (wtlRBTNode_Color(node) == eRED) {
// 此时需要再次对node进行递归地判断修复
return wtlRBTree_Balance(tree, node);
} else {
// 反之,对node的父节点进行递归地判断修复
return wtlRBTree_Balance(tree, wtlRBTNode_Parent(node));
}
}
// 设置根节点为黑色。因为每次插入一个节点,都是将其设置为红色的,而树的根节点必须为黑色
static void wtlRBTree_MakeBlack(wtlRBTREE* tree){
wtlRBTNode_SetColor(tree->root, eBLACK);
}
// 该函数的原理和二叉搜索树中介绍的是一样的,这里就不多讲了,可以点开头给的连接看下
// 注意,因为可能要改变root指向的一级指针的地址,所以要传个二级指针
static void wtlRBTree_InsertRaw(wtlRBT_NODE** root, wtlRBT_NODE* node){
if (NULL == *root) {
*root = node;
printf("insert:%ld\n", (*root)->value);
return;
} else {
node->parent = *root;
}
if ((*root)->value == node->value) {
return;
}
if ((*root)->value > node->value) {
wtlRBTree_InsertRaw(&((*root)->left), node);
} else {
wtlRBTree_InsertRaw(&((*root)->right), node);
}
}
// 红黑树完整的插入函数。tree为整棵树结构,value为要插入的值(因为节点只能存unsigned long类型的值...)
void wtlRBTree_Insert(wtlRBTREE* tree, unsigned long value){
// 创建一个节点用来存value
wtlRBT_NODE* node = wtlRBTNode_Create(value);
// 先按二叉搜索树的插入步骤将节点插入到树中
wtlRBTree_InsertRaw(&(tree->root), node);
// 然后再平衡,最后将根节点置黑
wtlRBTree_MakeBlack(wtlRBTree_Balance(tree, node));
}
简单吧。删除操作后面再加上 :)