自平衡linux红黑树

简介

实际应用中的自平衡搜索二叉树,除了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

显示的网页如下:
123

总结

本文简单介绍了红黑树的概念、算法实现,并进行了实践。
后续我有时间再慢慢补充相关知识点和可能遇到的问题。

posted @ 2022-05-20 03:20  李星云姬如雪  阅读(32)  评论(0编辑  收藏  举报