简介
实际应用中的自平衡搜索二叉树,除了AVL之外,红黑树也是备受宠爱。他不仅是linux中非线性结构的标准算法,而且是Java中TreeMap、TreeSet机制、C++中的STL这些经典工具背后的强大逻辑支撑。
与AVL不同,红黑树并不追求“绝对的平衡”,在毫无平衡性的BST和绝对平衡的AVL之间,红黑树聪明的做了折中,它的左右子树的高度差可以大于1,但任何一棵子树的高度不会大于另一棵兄弟子树高度的两倍。
正是红黑树放弃了AVL的绝对平衡的苛刻要求,让它获得了更加完美的性能表现。
复杂的逻辑并不意味着效率低,事实上红黑树的插入、删除、旋转、查找等操作都被控制在O(log2n)之中,对数级别的时间复杂度,使得红黑树尤其适用于数据无序程度高、数据量庞大且需要快速定位节点的场合。
AVL树是用树的高度差不大1这个绝对的条件来保证整棵树的平衡性的,而红黑树又是靠什么来保持二叉树的平衡性的?答案如下:
(1)树中的节点都是有颜色的,要么是红色,要么是黑色
(2)树的根节点是黑色的
(3)空节点的颜色算是黑色
(4)不能有连续的红色节点
(5)从任意一个节点开始到叶子的路径包含的黑色节点的个数相等
如果一棵二叉树满足以上条件,就会使得它不可能出现:一条路径的长度是另一条路径的两倍以上。
树的节点成员如下:
typedef struct _tree_node
{
tn_datatype data;
struct _tree_node *lchild; //左孩子指针
struct _tree_node *rchild; //右孩子指针
int height; //树的高度
struct _tree_node *parent; //指向父节点的指针
int color; //节点的颜色
#endif
对比AVL的节点设计,我们增加了树的父节点指针和颜色成员color,以及以该节点为根的子树的高度。
下面给出红黑树的算法实现。
算法实现
红黑树的公共操作文件rb_common.c,该文件主要提供旋转操作的接口,比如左旋、右旋、左右旋、右左旋,源代码如下:
rb_common.c
//
// Description: 红黑树的各种算法
//
//
#ifndef RB
#define RB
#endif
#include "drawtree.h"
#include "head4tree.h"
#include "head4rb.h"
// ================= 3,旋转操作 ================ //
void rb_rotate_left(linktree *proot, linktree n)
{
linktree gp = grandparent(n);
linktree p = n->parent;
p->rchild = n->lchild;
if(n->lchild != NULL)
n->lchild->parent = p;
n->lchild = p;
p->parent = n;
if(*proot == p)
*proot = n;
n->parent = gp;
if(gp != NULL)
{
if(p == gp->lchild)
gp->lchild = n;
else
gp->rchild = n;
}
}
void rb_rotate_right(linktree *proot, linktree n)
{
linktree gp = grandparent(n);
linktree p = n->parent;
p->lchild = n->rchild;
if(n->rchild != NULL)
n->rchild->parent = p;
n->rchild = p;
p->parent = n;
if(*proot == p)
*proot = n;
n->parent = gp;
if(gp != NULL)
{
if(p == gp->lchild)
gp->lchild = n;
else
gp->rchild = n;
}
}
void rb_rotate_leftright(linktree *proot, linktree n)
{
rb_rotate_left (proot, n);
rb_rotate_right(proot, n);
}
void rb_rotate_rightleft(linktree *proot, linktree n)
{
rb_rotate_right(proot, n);
rb_rotate_left (proot, n);
}
红黑树节点插入算法实现源码如下rb_insert.c:
//
// Description: 红黑树节点添加算法实现源码
//
//
#ifndef RB
#define RB
#endif
#include "drawtree.h"
#include "head4tree.h"
#include "head4rb.h"
void insert_fixup(linktree *proot, linktree new)
{
if(new->parent == NULL)
{
new->color = BLACK;
*proot = new;
return;
}
if(new->parent->color == BLACK) // 1: 黑父
return;
else
insert_case1(proot, new);
}
void insert_case1(linktree *proot, linktree new)
{
if(uncle(new) != NULL && uncle(new)->color == RED) // 2: 红父 + 红叔
{
new->parent->color = BLACK;
uncle(new)->color = BLACK;
grandparent(new)->color = RED;
insert_fixup(proot, grandparent(new));
}
else
insert_case2(proot, new);
}
void insert_case2(linktree *proot, linktree new) // 3: 红父 + 黑叔
{
if(new == new->parent->rchild &&
new->parent == grandparent(new)->lchild)
{
rb_rotate_left(proot, new);
new = new->lchild;
}
else if(new == new->parent->lchild &&
new->parent == grandparent(new)->rchild)
{
rb_rotate_right(proot, new);
new = new->rchild;
}
insert_case3(proot, new);
}
void insert_case3(linktree *proot, linktree new) // 3: 红父 + 黑叔
{
new->parent->color = BLACK;
grandparent(new)->color = RED;
if(new == new->parent->lchild &&
new->parent == grandparent(new)->lchild)
{
rb_rotate_right(proot, new->parent);
}
else
rb_rotate_left(proot, new->parent);
}
linktree bst_insert(linktree root, linktree new)
{
if(root == NULL)
return new;
new->parent = root;
if(new->data < root->data)
{
root->lchild = bst_insert(root->lchild, new);
}
else if(new->data > root->data)
{
root->rchild = bst_insert(root->rchild, new);
}
else
{
printf("%d exist.\n", new->data);
}
return root;
}
void rb_insert(linktree *proot, linktree new)
{
*proot = bst_insert(*proot, new);
insert_fixup(proot, new);
}
红黑树节点删除算法实现源码如下rb_delete.c:
//
// Description: 红黑树节点删除算法实现源码
//
//
#ifndef RB
#define RB
#endif
#include "drawtree.h"
#include "head4tree.h"
#include "head4rb.h"
linktree rb_find(linktree root, tn_datatype data)
{
if(root == NULL)
return NULL;
if(data < root->data)
return rb_find(root->lchild, data);
else if(data > root->data)
return rb_find(root->rchild, data);
return root;
}
void delete_fixup(linktree *proot, linktree new, linktree parent)
{
printf("%s\n", __FUNCTION__);
linktree ln, rn; // left nephew & right nephew
linktree s, gp; // sibling & grandparent
ln = rn = s = gp = NULL;
if(new == NULL && parent == NULL) // 原来的old是树都唯一节点
{
*proot = NULL;
return;
}
else if(new != NULL && parent == NULL) // 原来的old是根节点
{
*proot = new;
return;
}
else if(parent != NULL)
{
s = parent->lchild ? parent->lchild : parent->rchild;
gp = parent->parent;
if(s != NULL)
{
ln = s->lchild;
rn = s->rchild;
}
}
//1,红兄
if(Color(s) == RED)
{
if(new == parent->lchild)
{
rb_rotate_left(proot, s);
parent->color = RED;
s->color = BLACK;
delete_fixup(proot, new, parent);
}
if(new == parent->rchild)
{
rb_rotate_right(proot, s);
parent->color = RED;
s->color = BLACK;
delete_fixup(proot, new, parent);
}
}
//2,黑兄
if(Color(s) == BLACK)
{
//2.1,黑兄,二黑侄,红父
if(Color(parent) == RED &&
Color(ln) == BLACK &&
Color(rn) == BLACK)
{
parent->color = BLACK;
if(s != NULL)
s->color = RED;
return;
}
//2.2,黑兄,二黑侄,黑父
if(Color(parent) == BLACK &&
Color(ln) == BLACK &&
Color(rn) == BLACK)
{
if(s != NULL)
{
s->color = RED;
}
delete_fixup(proot, parent, parent->parent);
}
//2.3,黑兄,同边红侄(同为左孩子)
if(Color(ln) == RED && new == parent->lchild)
{
rb_rotate_right(proot, ln);
rb_rotate_left(proot, ln);
ln->color = parent->color;
parent->color = BLACK;
}
// (同为右孩子)
else if(Color(rn) == RED && new == parent->rchild)
{
rb_rotate_left(proot, rn);
rb_rotate_right(proot, rn);
rn->color = parent->color;
parent->color = BLACK;
}
// 对边红侄(左右)
else if(Color(ln) == RED && new == parent->rchild)
{
rb_rotate_right(proot, s);
s->color = parent->color;
parent->color = BLACK;
ln->color = BLACK;
}
// 对边红侄(右左)
else if(Color(rn) == RED && new == parent->lchild)
{
rb_rotate_left(proot, s);
s->color = parent->color;
parent->color = BLACK;
rn->color = BLACK;
}
}
}
void real_delete(linktree *proot, linktree old)
{
printf("%s\n", __FUNCTION__);
// old不可能为NULL,new可能为NULL
linktree new = old->lchild ? old->lchild : old->rchild;
linktree parent = old->parent;
if(old->parent != NULL)
{
if(old == old->parent->lchild)
old->parent->lchild = new;
else
old->parent->rchild = new;
old->parent = NULL;
}
if(new != NULL)
new->parent = old->parent;
if(Color(old) == BLACK && Color(new) == RED)
{
new->color = BLACK;
}
else if(Color(old) == BLACK && Color(new) == BLACK)
{
delete_fixup(proot, new, parent);
}
free(old);
}
void rb_delete(linktree *proot, tn_datatype data)
{
linktree tmp = rb_find(*proot, data);
if(tmp == NULL)
{
printf("%d is NOT exist.\n", data);
return;
}
linktree n = tmp;
if(tmp->lchild != NULL)
{
n = tmp->lchild;
for(;n->rchild != NULL; n = n->rchild);
tmp->data = n->data;
}
else if(tmp->rchild != NULL)
{
n = tmp->rchild;
for(;n->lchild != NULL; n = n->lchild);
tmp->data = n->data;
}
real_delete(proot, n); // n has ONE red-child at most
}
上述算法实现用到的头文件如下所示:
commonheader.h
#ifndef _COMMONHEADER_H_
#define _COMMONHEADER_H_
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <semaphore.h>
#include <fcntl.h>
#include <pthread.h>
#endif
head4queue.h
//
// Description: 本文件为队列核心头文件。
// 任何使用本队列算法的程序,在包含本头文件之前都需要
// 将如下宏定义成队列节点需要表达的数据类型:
//
// QUEUE_NODE_DATATYPE
//
// 否则队列的节点数据类型一律默认为 int
//
#ifndef _HEAD4QUEUE_H__
#define _HEAD4QUEUE_H__
#include "commonheader.h"
#ifndef QUEUE_NODE_DATATYPE
#define QUEUE_NODE_DATATYPE int
#endif
typedef QUEUE_NODE_DATATYPE qn_datatype;
struct _queue_node
{
qn_datatype data;
struct _queue_node *next;
};
typedef struct _queuenode
{
struct _queue_node *front;
struct _queue_node *rear;
#ifdef QUEUE_SIZE
int size;
#endif
}queuenode, *linkqueue;
bool is_empty_q(linkqueue);
bool out_queue(linkqueue, qn_datatype *);
bool en_queue(linkqueue, qn_datatype);
linkqueue init_queue(void);
#ifdef QUEUE_SIZE
int queue_size(linkqueue *);
#endif
#endif
head4rb.h
//
// Description: 红黑树的各种算法头文件
//
//
#ifndef _HEAD4RB_H_
#define _HEAD4RB_H_
#ifndef RB
#define RB
#endif
#include "head4tree.h"
static linktree grandparent(linktree n)
{
if(n == NULL || n->parent == NULL)
return NULL;
return n->parent->parent;
}
static linktree uncle(linktree n)
{
linktree gp = grandparent(n);
if(gp == NULL)
return NULL;
return n->parent == gp->lchild ?
gp->rchild : gp->lchild;
}
static linktree sibling(linktree n)
{
if(n == NULL || n->parent == NULL)
{
return NULL;
}
if(n == n->parent->lchild)
return n->parent->rchild;
else
return n->parent->lchild;
}
static linktree left_nephew(linktree n)
{
return sibling(n)==NULL ? NULL : sibling(n)->lchild;
}
static linktree right_nephew(linktree n)
{
return sibling(n)==NULL ? NULL : sibling(n)->rchild;
}
static int Color(linktree n)
{
return n==NULL ? BLACK : n->color;
}
void rotate_left(linktree *proot, linktree n);
void rotate_right(linktree *proot, linktree n);
linktree rb_find(linktree root, tn_datatype data);
linktree bst_insert(linktree root, linktree new);
void insert_case1(linktree *proot, linktree new);
void insert_case2(linktree *proot, linktree new);
void insert_case3(linktree *proot, linktree new);
void rb_insert(linktree *proot, linktree new);
void insert_fixup(linktree *proot, linktree new);
void rb_delete(linktree *proot, tn_datatype data);
void real_delete(linktree *proot, linktree old);
void delete_fixup(linktree *proot, linktree new, linktree parent);
#endif
head4tree.h
//
// Description: 本文件为二叉树核心头文件。
// 任何使用本二叉树结构算法的程序,在包含本头文件之前
// 都需要将如下宏定义成二叉树节点需要表达的数据类型:
//
// TREE_NODE_DATATYPE
//
// 否则二叉树的节点数据类型一律默认为 int
//
#ifndef _HEAD4TREE_H_
#define _HEAD4TREE_H_
/*
* Any application applying this linked-tree data structure should
* define the macro "TREE_NODE_DATATYPE" before include this head
* file, otherwise, the macro will be defined to 'int' as follow.
*
*/
#ifndef TREE_NODE_DATATYPE
#define TREE_NODE_DATATYPE int
#endif
#include "commonheader.h"
#define MAX(a, b) ({ \
typeof(a) _a = a; \
typeof(b) _b = b; \
(void)(&_a == &_b);\
_a > _b? _a : _b; \
})
typedef TREE_NODE_DATATYPE tn_datatype;
#ifdef RB
#define RED 0
#define BLACK 1
#endif
typedef struct _tree_node
{
tn_datatype data;
struct _tree_node *lchild;
struct _tree_node *rchild;
#ifdef AVL
int height;
#endif
#ifdef RB
struct _tree_node *parent;
int color;
#endif
}treenode, *linktree;
void pre_travel(linktree, void (*handler)(linktree));
void mid_travel(linktree, void (*handler)(linktree));
void post_travel(linktree, void (*handler)(linktree));
void level_travel(linktree, void (*handler)(linktree));
linktree bst_insert(linktree root, linktree new);
linktree bst_remove(linktree root, tn_datatype data);
linktree bst_find(linktree root, tn_datatype data);
#ifdef AVL
linktree avl_insert(linktree root, linktree new);
linktree avl_remove(linktree root, tn_datatype data);
linktree avl_rotate_left (linktree root);
linktree avl_rotate_right(linktree root);
linktree avl_rotate_leftright(linktree root);
linktree avl_rotate_rightleft(linktree root);
static int height(linktree root)
{
return root==NULL ? 0 : root->height;
}
#endif
static linktree new_node(tn_datatype data)
{
linktree new = malloc(sizeof(treenode));
if(new != NULL)
{
new->data = data;
new->lchild = NULL;
new->rchild = NULL;
#ifdef AVL
new->height = 1;
#endif
#ifdef RB
new->parent = NULL;
new->color = RED;
#endif
}
return new;
}
#endif
测试程序
设计一个程序,实现以下功能:
(1)输入大于0的数,则插入节点
(2)输入小于0的数,则删除节点
(3)输入0,则退出程序
(4)退出程序之前用网页画出该二叉树
示例代码如下:
rb_test.c
//
// Description: 使用红黑树操作接口实现的测试代码,测试结果用网页
// 展示。
//
#ifndef RB
#define RB
#endif
#include "drawtree.h"
#include "head4tree.h"
#include "head4rb.h"
int main(void)
{
linktree root = NULL;
printf("输入大于0的数插入节点\n");
printf("输入小于0的数删除节点\n");
printf("输入0退出程序\n");
int n;
while(1)
{
scanf("%d", &n);
if(n < 0)
{
rb_delete(&root, -n);
}
else if(n > 0)
{
linktree new = new_node(n);
rb_insert(&root, new);
}
else
break;
}
draw(root);
system("firefox *.html &");
return 0;
}
注意:上述测试程序使用了draw函数,用来在网页上画出二叉树,该函数相关的实现源码放在我的以下博文的drawtree.c文件中:
draw tree
运行效果如下所示:
输入大于0的数插入节点
输入小于0的数删除节点
输入0退出程序
1
2
3
4
5
9
12
18
91
45
30
28
82
9
9 exist.
13
14
15
66
76
0
显示的网页如下:
总结
本文简单介绍了红黑树的概念、算法实现,并进行了实践。
后续我有时间再慢慢补充相关知识点和可能遇到的问题。