博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
linux红黑树实现
阅读量:2352 次
发布时间:2019-05-10

本文共 2800 字,大约阅读时间需要 9 分钟。

Linux内核红黑树的算法都定义在linux-2.6.38.8/include/linux/rbtree.h和linux-2.6.38.8/lib/rbtree.c两个文件中。

    1、结构体 

[cpp]
  1. struct rb_node  
  2. {  
  3.     unsigned long  rb_parent_color;  
  4. #define RB_RED      0   
  5. #define RB_BLACK    1   
  6.     struct rb_node *rb_right;  
  7.     struct rb_node *rb_left;  
  8. } __attribute__((aligned(sizeof(long))));  
struct rb_node{	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_parent_color同时存储两种数据,一是其双亲结点的地址,另一是此结点的着色。__attribute__((aligned(sizeof(long))))属性保证了红黑树中的每个结点的首地址都是32位对齐的(在32位机上),也就是说每个结点首地址的bit[1]和bit[0]都是0,因此就可以使用bit[0]来存储结点的颜色属性而不干扰到其双亲结点首地址的存储。

PS: ptmalloc 代码中也用到这些特性,通过将一结构体进行地址对齐后,该结构体的首元素就可以节省出几个位用作他用。这种方法可以更高效地利用内存空间。

PS:为什么在rb_node中需要保存其父亲节点的首地址啊?因为内核中如果进行树的遍历,不可能用递归实现(递归容易造成栈溢出)。这样做,可以更好地转化为迭代来进行更好的树的遍历。

操作rb_parent_color的函数: 

[cpp]
  1. #define rb_parent(r)   ((struct rb_node *)((r)->rb_parent_color & ~3))  //获得其双亲结点的首地址  
  2. #define rb_color(r)   ((r)->rb_parent_color & 1) //获得颜色属性  
  3. #define rb_is_red(r)   (!rb_color(r))   //判断颜色属性是否为红  
  4. #define rb_is_black(r) rb_color(r) //判断颜色属性是否为黑  
  5. #define rb_set_red(r)  do { (r)->rb_parent_color &= ~1; } while (0)  //设置红色属性  
  6. #define rb_set_black(r)  do { (r)->rb_parent_color |= 1; } while (0) //设置黑色属性  
  7.   
  8. static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p)  //设置其双亲结点首地址的函数  
  9. {  
  10.     rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p;  
  11. }  
  12. static inline void rb_set_color(struct rb_node *rb, int color) //设置结点颜色属性的函数  
  13. {  
  14.     rb->rb_parent_color = (rb->rb_parent_color & ~1) | color;  
  15. }  

红黑树的遍历:

4、遍历

    rb_first和rb_next函数可组成中序遍历,即以升序遍历红黑树中的所有结点。 

[cpp]
  1. struct rb_node *rb_first(const struct rb_root *root)  
  2. {  
  3.     struct rb_node  *n;  
  4.   
  5.     n = root->rb_node;  
  6.     if (!n)  
  7.         return NULL;  
  8.     while (n->rb_left)  
  9.         n = n->rb_left;  
  10.     return n;  
  11. }  
  12.   
  13. struct rb_node *rb_next(const struct rb_node *node)  
  14. {  
  15.     struct rb_node *parent;  
  16.   
  17.     if (rb_parent(node) == node)  
  18.         return NULL;  
  19.   
  20.     /* If we have a right-hand child, go down and then left as far 
  21.        as we can. */  
  22.     if (node->rb_right) {  
  23.         node = node->rb_right;   
  24.         while (node->rb_left)  
  25.             node=node->rb_left;  
  26.         return (struct rb_node *)node;  
  27.     }  
  28.   
  29.     /* No right-hand children.  Everything down and left is 
  30.        smaller than us, so any 'next' node must be in the general 
  31.        direction of our parent. Go up the tree; any time the 
  32.        ancestor is a right-hand child of its parent, keep going 
  33.        up. First time it's a left-hand child of its parent, said 
  34.        parent is our 'next' node. */  
  35.     while ((parent = rb_parent(node)) && node == parent->rb_right)  
  36.         node = parent;  
  37.   
  38.     return parent;  
  39. }  

一个遍历的演示程序:

  1. void print_rbtree(struct rb_root *tree)  
  2. {  
  3.     struct rb_node *node;  
  4.       
  5.     for (node = rb_first(tree); node; node = rb_next(node))  
  6.     printf("%d ", rb_entry(node, struct mytype, my_node)->num);  
  7.       
  8.     printf("\n");  
  9. }  

PS:linux内核中将这种遍历做成了STL迭代器的形式,从而更好地进行代码的复用。

PS:感觉linux 内核中还有些 for_each宏之类的东西,是想做成闭包的形式,也是模仿STL的思想,更好地进行代码复用。

转载地址:http://skrvb.baihongyu.com/

你可能感兴趣的文章
如何在linux CentOS 上安装chrome 谷歌浏览器
查看>>
laravel5 怎么实现事务
查看>>
GitLab安装说明
查看>>
Git查看、删除、重命名远程分支和tag
查看>>
PHP类中的抽象类,抽象方法,abstract
查看>>
PHP接口类interface的正确使用方法
查看>>
Sencha Touch之Hello World
查看>>
Tab Layout 之单个Activity实现
查看>>
Tab Layout 之多个Activity实现
查看>>
FrameLayout之我见
查看>>
个人解读Activity之一
查看>>
实现自定义布局的Notification
查看>>
AlarmManager的学习与实现
查看>>
解读Content Provider之一
查看>>
解读Content Provider之二
查看>>
自定义UI实例
查看>>
推荐一个不错的自定义UI
查看>>
fedora16 设置 gedit软件的默认编码
查看>>
S3C6410 存储器映射
查看>>
Linux 3.3.0移植到S3C6410开发板上之一
查看>>