红黑树详解(源码+图示)

Rbtree.h文件中数据结构及函数

rb_node

红黑树节点数据结构中使用成员rb_parent_color同时存储两种数据,一是其双亲结点的地址,另一是此结点的着色。__attribute__((aligned(sizeof(long))))属性保证了红黑树中的每个结点的首地址都是32位对齐的(在32位机上),也就是说每个结点首地址的bit[1]bit[0]都是0,因此就可以使用bit[0]来存储结点的颜色属性而不干扰到其双亲结点首地址的存储。

struct rb_node

{

 /*30位存储双亲节点地址,最后一位为此节点颜色,低两位默认为0,表示为红色,若最后一位为1则表示为黑色*/

    unsigned long  rb_parent_color;  

#define RB_RED 0

#define RB_BLACK 1

struct rb_node *rb_right;     /*右子节点*/

struct rb_node *rb_left;      /*左子结点*/

} __attribute__((aligned(sizeof(long))));    

rb_root

/*红黑树的根节点*/

struct rb_root

{

struct rb_node *rb_node;

};

rb_parent_color操作函数

/*获取双亲节点的地址和此节点颜色,& ~3表示将倒数两位位清0,其余位保持不变*/

#define rb_parent(r)   ((struct rb_node *)((r)->rb_parent_color & ~3))

/*获取此节点的颜色,最低位表示颜色,1表示黑色,0表示红色*/

#define rb_color(r)   ((r)->rb_parent_color & 1)

/*判断此节点是否为红色*/

#define rb_is_red(r)   (!rb_color(r))

/*判断此节点是否为黑色*/

#define rb_is_black(r) rb_color(r)

/*将此节点设置为红色,&~1表示将最后一位清0,其余位不变*/

#define rb_set_red(r)  do { (r)->rb_parent_color &= ~1; } while (0)

/*将此节点设置为黑色*/

#define rb_set_black(r)  do { (r)->rb_parent_color |= 1; } while (0)

rb_set_parent()

/*设置双亲节点地址的函数,首先&3保持低两位不变,将高30位清0,然后将P设置为双亲地址*/

static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p)

{

rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p;

}

rb_set_color()

/*设置结点颜色属性的函数,先&~1将最低位清0,再获取color的颜色属性*/

static inline void rb_set_color(struct rb_node *rb, int color)

{

rb->rb_parent_color = (rb->rb_parent_color & ~1) | color;

}

 

/*初始化指向红黑树根结点的指针*/

#define RB_ROOT (struct rb_root) { NULL, }

/*用来获得包含struct rb_node的结构体的首地址 */

#define rb_entry(ptr, type, member) container_of(ptr, type, member)

/*判断树是否为空*/

#define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL)

/*判断节点的双亲节点是否为自身*/

#define RB_EMPTY_NODE(node) (rb_parent(node) == node)

/*设置节点的双亲节点为自身*/

#define RB_CLEAR_NODE(node) (rb_set_parent(node, node))

rb_init_node()

/*初始化新节点*/

static inline void rb_init_node(struct rb_node *rb)

{

rb->rb_parent_color = 0;

rb->rb_right = NULL;

rb->rb_left = NULL;

RB_CLEAR_NODE(rb);

}

rb_link_node()

/*将节点作为子节点连入红黑树*/

static inline void rb_link_node(struct rb_node * node, struct rb_node * parent,

struct rb_node ** rb_link)

{

/*设置其双亲结点的首地址(根结点的双亲结点为NULL),此结点颜色默认为红色*/

    node->rb_parent_color = (unsigned long )parent;

node->rb_left = node->rb_right = NULL;

/*rb_link一般在调用前,就说明了是parent的左叶还是右叶 */

*rb_link = node;

}

Rbtree.c文件中函数

当在红黑树上进行插入和删除操作时,对树做了修改,结果可能会违反红黑树的那五条性质。为了保持这些性质,就要改变树中某些结点的颜色和指针结构。

    指针结构的修改时通过旋转来完成的,这是一种能保持二叉查找树性质的查找树局部操作:即左旋(右子为轴,当前结点左旋)和右旋(左子为轴,当前结点右旋:

 

左旋:比如说,需要把x旋转为y的左结点。整个算法的思路非常清晰:从上至下,先得到y指针,将x的右指针指向y的左结点,然后利用parent函数得到x的父亲结点,如果为NULL,则y为新的根,如果不为NULL,则根据x是其父亲的左孩子还是右孩子,将指针指向y。最后将y的左指针指向x,完成旋转。值得注意的是,算法是具有顺序的逻辑步骤,不能够调换顺序,如果改变赋值的顺序会造成内存失去指针指向,出现内存错误。

个人理解:假如X左旋,就是把X的右孩子Y作为X的父节点,且XY的左孩子,Y原来的左孩子成为X的右孩子,填补Y的空缺。如果是X右旋的话,就是把X的左孩子Y作为X的父节点,且XY的右孩子,Y原来的右孩子成为X的左孩子。更简洁明了一点左旋就是交换右子树和父节点,右旋就是交换左子树和父节点。

__rb_rotate_left()

/*结点左旋操作,右子为轴,当前结点左旋,node为进行左旋操作的结点,旋转前后对比上图*/

static void __rb_rotate_left(struct rb_node *node, struct rb_root *root)

{

/*先获得待左旋结点node的右子结点right和双亲节点parent*/

    struct rb_node *right = node->rb_right; 

struct rb_node *parent = rb_parent(node);

    

    /*right的左子节点转为node的右子结点

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值