c语言中while是红色,浅谈红黑树(C语言代码实现)

定义:

我们先来看看《算法导论》中的红黑树的定义:“红黑树是许多‘平衡’搜索树的一种,可以保证在最坏的情况下基本动态集合操作的时间复杂度为O(lgn)。”

性质:

红黑树的性质如下:

1、每个节点是红色的,或者是黑色的。

2、根节点和叶子节点是黑色的。

3、如果一个节点是红色的,那么它的父节点和子节点必须是黑色的。

4、对于每一个节点来说,从该节点到叶子节点的简单路径上,所包含的黑色节点的数量必须一致。

节点属性:

红黑树每个节点的基本属性值有五个:

1、color:表示该节点的颜色属性。

2、data:表示该节点的数据域。

3、parent:表示该节点的父节点。

4、left_child:表示该节点的左子结点。

5、right_child:表示该节点的右子结点。

以下代码的红黑树节点属性信息:typedef struct rbnode {

int           data   ;

int           color  ;

struct rbnode *left_child     ;

struct rbnode *right_child     ;

struct rbnode *parent      ;

} Rbnode;

typedef Rbnode *Rbtree;

插入操作:

插入操作包含以下三个步骤:

1、查找操作。为待插入节点寻找合适的位置。

红黑树的节点的数据域的规律满足平衡二叉树的节点数据与的规律,也就是某节点的左子结点的数据域小于该节点的数据域,同时该节点的右子结点的数据域大于该节点的数据域。

根据以上数据域的规律可以找到带插入节点的位置,若待插入节点的数据域已经存在于树中,可以选择继续插入,也可以直接返回,该处,我选择若存在就直接返回,若该节点的数据域在该树中不存在,就返回查找到的待测节点的父节点。

代码如下:

static Rbnode *rbtree_search_auxilary(Rbtree root, int data, Rbnode **parent)

{

Rbnode *node = NULL, *sparent;

if(NULL == root){

return NULL;

}

node= root;

while(node){

sparent = node->parent;

if(data == node->data){

return node;

}else if(data > node->data){

node = node->right_child;

}else{

node = node->left_child;

}

}

if(parent){

*parent = sparent;

}

return NULL;

}

2、插入待测节点操作。

由性质4可以得出,为了使新插入节点尽量不破坏红黑树的平衡,新插入节点的颜色必须为红色。

代码如下:

Rbtree rbtree_insert(Rbtree root, int data)

{

Rbtree tree  = NULL;

Rbnode *node =  NULL, *parent = NULL;

// analyse whether newnode's data in this tree, if yes, you need't insert it.

if(root){

node = rbtree_search_auxilary(root, data, &parent);

if(node){

printf("%d already in this tree\n", data);

return root;

}

}

// insert data in tree;

node = buy_node(); // function 'buy_node' just malloc a space for new_node;

node->data = data;

node->color = RED;

node->parent = parent;

node->left_child = node->right_child = NULL;

if(parent){

if(parent->left_child){

parent->left_child = node;

}else{

parent->right_child = node;

}

}else{

root = node;

}

return rbtree_insert_rebalance(root, node);

}

3、平衡红黑树操作。

若新插入节点的父节点为黑色节点,那么该树满足红黑树的性质,不需要调整。若新插入节点的父节点为红色节点,此时违背了红黑树的性质3,需要进一步平衡红黑树。

平衡插入操作有五种情况:

1.父节点P,叔叔节点U,都是红色;

2.父节点P是红色,叔叔节点U是黑色或者NULL,且N为右孩子

3.父节点P是红色,叔叔节点U是黑色或者NULL,且N为左孩子

代码如下:

static Rbtree rbtree_insert_rebalance(Rbtree root, Rbnode *node)

{

Rbnode *gparent = NULL, *parent = NULL, *uncle = NULL;

if(NULL == root || NULL == node){

return NULL;

}

while((parent = node->parent) && RED == parent->color){

gparent = parent->parent;

if(gparent){

if(parent == gparent->left_child){

uncle = gparent->right_child;

// case 1,uncle is red;

if(uncle && RED == uncle->color){

parent->color = BLACK;

uncle->color = BLACK;

gparent->color = RED;

node = gparent;

}else{

/*case2 & case3, uncle is black.

case2: if new_node is parent's right_node, becase parent is gparent's

left_child, this case belongs to inner_insert, so, it is need

double_rotate, right_rotate -> left_rotate.

case 3: new_node is parent;s left_node, its belongs to outer_insert,

just need left_rotate.

accrounding foregoing analysis, case2 contains case3.*/

if(node == parent->right_child){

root = rbtree_rotate_left(root, parent);

swap(&parent, &node, sizeof(parent));

}

parent->color = BLACK;

gparent->color = RED;

root = rbtree_rotate_right(root, gparent);

}

}else{

// the same as foregoing operating.

uncle = gparent->left_child;

if(uncle && RED == uncle->color){

uncle->color = BLACK;

parent->color = BLACK;

gparent->color = RED;

node = gparent;

}else{

if(node == parent->left_child){

root = rbtree_rotate_right(root, parent);

swap(&parent, &node, sizeof(parent));

}

parent->color = BLACK;

gparent->color = RED;

root = rbtree_rotate_left(root, gparent);

}

}

}

}

root->color = BLACK;

return root;

}

由于在平衡过程中由旋转(即左旋、右旋)的过程,还有工具函数swap的使用,附上源码:

旋转的过程可能是我们最头疼的问题,因为搞不懂指针的指向,在这里,我与大家分享一下小技巧,在旋转的过程中我们一步一步来。

首先,旋转的过程中解决子节点问题。若右旋过程中,节点存在右子结点,将右子结点托管其父节点,作为父节点的左子结点,左旋则相反。

其次,旋转过程中解决父节点的问题。无论左旋还是右旋其父节点,均都会作为该旋转节点的子节点。

最后,解决节点本身的问题。节点的父节点是哪个?其父节点为该节点父节点的父节点,若父节点的父节点不存在,该节点为root。节点的子节点是哪个?若右旋过程中,父节点作为其右子结点,左子结点不变;左旋相反。

左旋右旋的代码如下:

static Rbtree rbtree_rotate_left(Rbnode *node, Rbtree root)

{

Rbnode *right = node->right_child;

// child problem

if(node->right_child = right->left_child){

right->left_child->parent = node;

}

// parent problem

if(right->parent = node->parent){

if(node == node->parent->right_child){

node->parent->right_child = right;

}else{

node->parent->left_child = right;

}

}else{

root = right;

}

// self problem

right->left_child = node;

node->parent = right;

return root;

}

static Rbtree rbtree_rotate_right(Rbnode *node, Rbtree root)

{

Rbnode *left = node->left_child;

// child's problem

if(node->left_child = left->right_child){

left->right_child->parent = node;

}

// parent's problem

if(left->parent = node->parent){

if(node == node->parent->right_child){

node->parent->right_child = left;

}else{

node->parent->left_child = left;

}

}else{

root = left;

}

// self's problem

left->right_child = node;

node->parent = left;

return root;

}

swap函数源码:

void swap(void *a, void *b, size_t size)

{

void *tmp;

tmp = (void *)Malloc(size);

memcpy(tmp, a, size);

memcpy(a, b , size);

memcpy(b, tmp, size);

free(tmp);

}

删除操作:

删除操作与插入操作类似也分为三步操作:

1、找出待删除节点。其查找方法与插入操作的查找节点的方法一致,不再做过多赘述。

2、若节点存在就删除该节点。

情况一:待删除节点有两个子节点。

情况二:待删除节点有一个子节点或者不存在子节点。

情况一可以转换成情况二,当待删除节点存在两个子节点时,可以用左子树的最大节点或者右子树的最小节点替换该节点,此时,只需要删除左子树的最大节点或者右子树的最小节点即可。

代码如下:

Rbtree rbtree_delete(Rbtree root, int data)

{

Rbnode *parent = NULL, *child = NULL;

Rbnode *node   = NULL, *old   = NULL;

int    color   = RED;

// find the delete-node

node = rbtree_search_auxilary(root, data, NULL);

if(!node){

fprintf(stderr, "%d does not in the tree\n");

return NULL;

}

// remember the delete-node

old = node;

/*

* case 1 : the delete-node has only-one child, you just

*      let node's parent adapt you child;

* case 2 : the delete-node has two children, you can find

*      max-left_child or min-right_child to success your

*      property & support your parent.

* Then case 2 can switch to case 1.

*/

// case 2

if(node->left_child && node->right_child){

// we choose its min-right_child

node = node->right_child;

node = find_min_node(node);

// you should solve the min-right_child's child & parent

child = node->right_child;

parent = node->parent;

color = node->color;

// child first

if(child){

child->parent = parent;

}

// parent sencond

if(parent){

if(node == parent->left_child){

parent->left_child = child;

}else{

parent->right_child = child;

}

}else{

root = child;

}

if(node->parent == old){

parent = node;

}

// the use min-right_child to replace the delete-node

node->parent = old->parent;

node->right_child = old->right_child;

node->left_child = old->left_child;

node->color = old->color;

if(old->parent){

if(old == old->parent->left_child){

old->parent->left_child = node;

}else{

old->parent->right_child = node;

}

}else{

root = node;

}

old->left_child->parent = node;

if(old->right_child){

old->right_child->parent = node;

}

}else{

// case 1 : only has one child or has no->child

if(node->left_child){

child = node->left_child;

}else{

child = node->right_child;

}

parent = node->parent;

color = node->color;

if(child){

child->parent = parent;

}

if(parent){

if(node == parent->left_child){

parent->left_child = child;

}else{

parent->right_child = child;

}

}else{

root = child;

}

}

free(old);

if(BLACK == color){

return rbtree_delete_rebalance(root, child, parent);

}

return root;

}

3、删除节点后平衡红黑树。

若删除的节点是红色,并没有破坏红黑树的平衡,不需要做平衡操作,若删除节点的黑色节点,是通过该节点的路径比不通过该节点大的路径减少一个黑色节点,违反了红黑树的性质4。

删除操作有以下四种情况:

1.当前节点是黑色,当前兄弟节点为红色(此时父节点和兄弟节点的子节点为黑色);

2.当前节点是黑色,且当前节点的兄弟节点是黑色,并且兄弟节点的两个子节点全为黑色;

3.当前节点是黑色,兄弟节点是黑色,兄弟节点的左孩子是红色,右孩子是黑色;

4.当前节点是黑色,兄弟节点是黑色,兄弟节点的右孩子是红色,左孩子颜色任意;

具体平衡操作代码如下:

static Rbtree rbtree_delete_rebalance(Rbtree root, Rbnode *node, Rbnode *parent)

{

Rbnode *bro = NULL;

Rbnode *bro_left = NULL, *bro_right = NULL;

while((NULL == node || BLACK == node->color) && node != root){

if(node == parent->left_child){

bro = parent->right_child;

// case 1 : bro is Red

if(bro && RED == bro->color){

bro->color = BLACK;

bro->parent = RED;

root = rbtree_rotate_left(parent, root);

bro = parent->left_child;

}

if((!bro->left_child || BLACK == bro->left_child->color) &&

(!bro->right_child || BLACK == bro->right_child->color)){

// case 2 : bro is BLACK & its children are all BLACK

bro->color = RED;

node = parent;

parent = node->parent;

}else{

// case 3 : bro is BLACK & its left_child is RED & bro_right is BLACK

if(NULL == bro->right_child || BLACK == bro->right_child->color){

if(bro_left = bro->left_child){

bro_left->color = BLACK;

}

bro->color = RED;

root = rbtree_rotate_right(bro, root);

bro = parent->right_child;

}

// case 4 : bro is BLACK & its right_child is RED;

bro->color = parent->color;

parent->color = BLACK;

if(bro->right_child){

bro->right_child->color = BLACK;

}

root = rbtree_rotate_left(parent, root);

node = root;

break;

}

}else{

bro = parent->left_child;

//  case 1

if(bro && RED == bro->color){

bro->color = BLACK;

bro->parent = RED;

root = rbtree_rotate_right(parent, root);

bro = parent->left_child;

}

// case 2

if((!bro->left_child || BLACK == bro->left_child->color) &&

(!bro->right_child) || BLACK == bro->right_child->color){

bro->color = RED;

node = parent;

parent = node->parent;

}else{

// case 3

if(!bro->right_child || BLACK == bro->right_child->color){

if(bro_left = bro->left_child){

bro_left->color = BLACK;

}

bro->color = RED;

root = rbtree_insert_rebalance(bro, root);

node = parent->left_child;

}

// case 4

bro->color = parent->color;

parent->color = BLACK;

if(bro->right_child){

bro->right_child->color = BLACK;

}

root = rbtree_rotate_left(parent,root);

node = root;

break;

}

}

}

if(node){

node->color = BLACK;

}

return root;

}

由于以上代码中牵扯寻找左子树的最大节点,右子树的最小节点,以下是实现源码:

Rbnode *find_min_node(Rbtree root)

{

Rbnode *node = root;

if(NULL == root){

return NULL;

}

while(node->left_child){

node = node->left_child;

}

return node;

}

Rbnode *find_max_node(Rbtree root)

{

Rbnode *node = root;

if(NULL == root){

return NULL;

}

while(node->right_child){

node = node->right_child;

}

return node;

}

以上是本人对红黑树的简单理解,如有不妥之处,欢迎大家指出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值