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的父节点,且X是Y的左孩子,Y原来的左孩子成为X的右孩子,填补Y的空缺。如果是X右旋的话,就是把X的左孩子Y作为X的父节点,且X是Y的右孩子,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的右子结点 |