详解Linux内核红黑树算法的实现

标签: linux内核structinsertnulltree算法
2012-04-11 17:02 14253人阅读 评论(7) 收藏 举报
 分类:
linux内核修炼之其它(12) 基本算法(10) 

版权声明:本文为博主原创文章,未经博主允许不得转载。

开发平台:Ubuntu11.04

内核源码:linux-2.6.38.8.tar.bz2

关于二叉查找树的概念请参考博文《详解二叉查找树算法的实现》。

平衡二叉树(BalancedBinary Tree或Height-Balanced Tree)又称AVL树。它或者是一棵空树,或者是具有下列性质的二叉树:它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1。若将二叉树上结点的平衡因子BF(BalanceFactor)定义为该结点的左子树的深度减去它的右子树的深度,则平衡二叉树上所有结点的平衡因子只可能是-1、0和1。(此段定义来自严蔚敏的《数据结构(C语言版)》)

红黑树是一种在插入或删除结点时都需要维持平衡的二叉查找树,并且每个结点都具有颜色属性:

(1)、一个结点要么是红色的,要么是黑色的。

(2)、根结点是黑色的。

(3)、如果一个结点是红色的,那么它的子结点必须是黑色的,也就是说在沿着从根结点出发的任何路径上都不会出现两个连续的红色结点。

(4)、从一个结点到一个NULL指针的每条路径上必须包含相同数目的黑色结点。

(此图片来自维基百科)

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

1、结构体

[cpp] view plaincopy
  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))));

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

操作rb_parent_color的函数:

[cpp] view plaincopy
  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. static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p)  //设置其双亲结点首地址的函数
  8. {
  9. rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p;
  10. }
  11. static inline void rb_set_color(struct rb_node *rb, int color) //设置结点颜色属性的函数
  12. {
  13. rb->rb_parent_color = (rb->rb_parent_color & ~1) | color;
  14. }

初始化新结点:

[cpp] view plaincopy
  1. static inline void rb_link_node(struct rb_node * node, struct rb_node * parent,
  2. struct rb_node ** rb_link)
  3. {
  4. node->rb_parent_color = (unsigned long )parent;   //设置其双亲结点的首地址(根结点的双亲结点为NULL),且颜色属性设为黑色
  5. node->rb_left = node->rb_right = NULL;   //初始化新结点的左右子树
  6. *rb_link = node;  //指向新结点
  7. }

指向红黑树根结点的指针:

[cpp] view plaincopy
  1. struct rb_root
  2. {
  3. struct rb_node *rb_node;
  4. };
  5. #define RB_ROOT (struct rb_root) { NULL, }  //初始化指向红黑树根结点的指针
  6. #define rb_entry(ptr, type, member) container_of(ptr, type, member) //用来获得包含struct rb_node的结构体的首地址
  7. #define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL) //判断树是否为空
  8. #define RB_EMPTY_NODE(node) (rb_parent(node) == node)  //判断node的双亲结点是否为自身
  9. #define RB_CLEAR_NODE(node) (rb_set_parent(node, node)) //设置双亲结点为自身

2、插入

首先像二叉查找树一样插入一个新结点,然后根据情况作出相应的调整,以使其满足红黑树的颜色属性(其实质是维持红黑树的平衡)。

函数rb_insert_color使用while循环不断地判断双亲结点是否存在,且颜色属性为红色。

若判断条件为真,则分成两部分执行后续的操作:

(1)、当双亲结点是祖父结点左子树的根时,则:

a、存在叔父结点,且颜色属性为红色。

b、当node是其双亲结点右子树的根时,则左旋,然后执行第c步。

c、当node是其双亲结点左子树的根时。

(2)、当双亲结点是祖父结点右子树的根时的操作与第(1)步大致相同,这里略过不谈。

若为假,则始终设置根结点的颜色属性为黑色。

[cpp] view plaincopy
  1. void rb_insert_color(struct rb_node *node, struct rb_root *root)
  2. {
  3. struct rb_node *parent, *gparent;
  4. while ((parent = rb_parent(node)) && rb_is_red(parent)) //双亲结点不为NULL,且颜色属性为红色
  5. {
  6. gparent = rb_parent(parent); //获得祖父结点
  7. if (parent == gparent->rb_left) //双亲结点是祖父结点左子树的根
  8. {
  9. {
  10. register struct rb_node *uncle = gparent->rb_right; //获得叔父结点
  11. if (uncle && rb_is_red(uncle)) //叔父结点存在,且颜色属性为红色
  12. {
  13. rb_set_black(uncle); //设置叔父结点为黑色
  14. rb_set_black(parent); //设置双亲结点为黑色
  15. rb_set_red(gparent); //设置祖父结点为红色
  16. node = gparent;  //node指向祖父结点
  17. continue; //继续下一个while循环
  18. }
  19. }
  20. if (parent->rb_right == node)  //当node是其双亲结点右子树的根时
  21. {
  22. register struct rb_node *tmp;
  23. __rb_rotate_left(parent, root); //左旋
  24. tmp = parent;  //调整parent和node指针的指向
  25. parent = node;
  26. node = tmp;
  27. }
  28. rb_set_black(parent); //设置双亲结点为黑色
  29. rb_set_red(gparent); //设置祖父结点为红色
  30. __rb_rotate_right(gparent, root); //右旋
  31. } else { // !(parent == gparent->rb_left)
  32. {
  33. register struct rb_node *uncle = gparent->rb_left;
  34. if (uncle && rb_is_red(uncle))
  35. {
  36. rb_set_black(uncle);
  37. rb_set_black(parent);
  38. rb_set_red(gparent);
  39. node = gparent;
  40. continue;
  41. }
  42. }
  43. if (parent->rb_left == node)
  44. {
  45. register struct rb_node *tmp;
  46. __rb_rotate_right(parent, root);
  47. tmp = parent;
  48. parent = node;
  49. node = tmp;
  50. }
  51. rb_set_black(parent);
  52. rb_set_red(gparent);
  53. __rb_rotate_left(gparent, root);
  54. } //end if (parent == gparent->rb_left)
  55. } //end while ((parent = rb_parent(node)) && rb_is_red(parent))
  56. rb_set_black(root->rb_node);
  57. }

3、删除

像二叉查找树的删除操作一样,首先需要找到所需删除的结点,然后根据该结点左右子树的有无分为三种情形:

若node结点的颜色属性为黑色,则需要调用__rb_erase_color函数来进行调整。

[cpp] view plaincopy
  1. void rb_erase(struct rb_node *node, struct rb_root *root)
  2. {
  3. struct rb_node *child, *parent;
  4. int color;
  5. if (!node->rb_left) //删除结点无左子树
  6. child = node->rb_right;
  7. else if (!node->rb_right) //删除结点无右子树
  8. child = node->rb_left;
  9. else //左右子树都有
  10. {
  11. struct rb_node *old = node, *left;
  12. node = node->rb_right;
  13. while ((left = node->rb_left) != NULL)
  14. node = left;
  15. if (rb_parent(old)) {
  16. if (rb_parent(old)->rb_left == old)
  17. rb_parent(old)->rb_left = node;
  18. else
  19. rb_parent(old)->rb_right = node;
  20. } else
  21. root->rb_node = node;
  22. child = node->rb_right;
  23. parent = rb_parent(node);
  24. color = rb_color(node);
  25. if (parent == old) {
  26. parent = node;
  27. } else {
  28. if (child)
  29. rb_set_parent(child, parent);
  30. parent->rb_left = child;
  31. node->rb_right = old->rb_right;
  32. rb_set_parent(old->rb_right, node);
  33. }
  34. node->rb_parent_color = old->rb_parent_color;
  35. node->rb_left = old->rb_left;
  36. rb_set_parent(old->rb_left, node);
  37. goto color;
  38. }  //end else
  39. parent = rb_parent(node); //获得删除结点的双亲结点
  40. color = rb_color(node); //获取删除结点的颜色属性
  41. if (child)
  42. rb_set_parent(child, parent);
  43. if (parent)
  44. {
  45. if (parent->rb_left == node)
  46. parent->rb_left = child;
  47. else
  48. parent->rb_right = child;
  49. }
  50. else
  51. root->rb_node = child;
  52. color:
  53. if (color == RB_BLACK) //如果删除结点的颜色属性为黑色,则需调用__rb_erase_color函数来进行调整
  54. __rb_erase_color(child, parent, root);
  55. }

4、遍历

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

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

5、在应用程序中使用

Linux内核中红黑树算法的实现非常通用、巧妙,而且免费又开源,因此完全可以把它运用到自己的应用程序中。

(1)、从内核中拷贝源文件:

[cpp] view plaincopy
  1. $ mkdir redblack
  2. $ cd redblack/
  3. $ cp ../linux-2.6.38.8/lib/rbtree.c .
  4. $ cp ../linux-2.6.38.8/include/linux/rbtree.h .

(2)、修改源文件:

a、C文件rbtree.c

修改包含头文件的代码

[cpp] view plaincopy
  1. //删除以下两行代码
  2. #include <linux/rbtree.h>
  3. #include <linux/module.h>
  4. //新增以下代码,即包含当前目录中的头文件rbtree.h
  5. #include "rbtree.h"

删除所有的EXPORT_SYMBOL宏

[cpp] view plaincopy
  1. EXPORT_SYMBOL(rb_insert_color);
  2. EXPORT_SYMBOL(rb_erase);
  3. EXPORT_SYMBOL(rb_augment_insert);
  4. EXPORT_SYMBOL(rb_augment_erase_begin);
  5. EXPORT_SYMBOL(rb_augment_erase_end);
  6. EXPORT_SYMBOL(rb_first);
  7. EXPORT_SYMBOL(rb_last);
  8. EXPORT_SYMBOL(rb_next);
  9. EXPORT_SYMBOL(rb_prev);
  10. EXPORT_SYMBOL(rb_replace_node);

b、头文件rbtree.h

删除包含头文件的代码,并添加三个宏定义

[cpp] view plaincopy
  1. //删除以下两行代码
  2. #include <linux/kernel.h>
  3. #include <linux/stddef.h>
  4. /* linux-2.6.38.8/include/linux/stddef.h */
  5. #undef NULL
  6. #if defined(__cplusplus)
  7. #define NULL 0
  8. #else
  9. #define NULL ((void *)0)
  10. #endif
  11. /* linux-2.6.38.8/include/linux/stddef.h */
  12. #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
  13. /* linux-2.6.38.8/include/linux/kernel.h */
  14. #define container_of(ptr, type, member) ({          \
  15. const typeof( ((type *)0)->member ) *__mptr = (ptr); \
  16. (type *)( (char *)__mptr - offsetof(type,member) );})

(3)、示例代码

Linux内核红黑树的使用方法请参考linux-2.6.38.8/Documentation/rbtree.txt文件。

[cpp] view plaincopy
  1. /* test.c */
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include "rbtree.h"
  5. struct mytype {
  6. struct rb_node my_node;
  7. int num;
  8. };
  9. struct mytype *my_search(struct rb_root *root, int num)
  10. {
  11. struct rb_node *node = root->rb_node;
  12. while (node) {
  13. struct mytype *data = container_of(node, struct mytype, my_node);
  14. if (num < data->num)
  15. node = node->rb_left;
  16. else if (num > data->num)
  17. node = node->rb_right;
  18. else
  19. return data;
  20. }
  21. return NULL;
  22. }
  23. int my_insert(struct rb_root *root, struct mytype *data)
  24. {
  25. struct rb_node **tmp = &(root->rb_node), *parent = NULL;
  26. /* Figure out where to put new node */
  27. while (*tmp) {
  28. struct mytype *this = container_of(*tmp, struct mytype, my_node);
  29. parent = *tmp;
  30. if (data->num < this->num)
  31. tmp = &((*tmp)->rb_left);
  32. else if (data->num > this->num)
  33. tmp = &((*tmp)->rb_right);
  34. else
  35. return -1;
  36. }
  37. /* Add new node and rebalance tree. */
  38. rb_link_node(&data->my_node, parent, tmp);
  39. rb_insert_color(&data->my_node, root);
  40. return 0;
  41. }
  42. void my_delete(struct rb_root *root, int num)
  43. {
  44. struct mytype *data = my_search(root, num);
  45. if (!data) {
  46. fprintf(stderr, "Not found %d.\n", num);
  47. return;
  48. }
  49. rb_erase(&data->my_node, root);
  50. free(data);
  51. }
  52. void print_rbtree(struct rb_root *tree)
  53. {
  54. struct rb_node *node;
  55. for (node = rb_first(tree); node; node = rb_next(node))
  56. printf("%d ", rb_entry(node, struct mytype, my_node)->num);
  57. printf("\n");
  58. }
  59. int main(int argc, char *argv[])
  60. {
  61. struct rb_root mytree = RB_ROOT;
  62. int i, ret, num;
  63. struct mytype *tmp;
  64. if (argc < 2) {
  65. fprintf(stderr, "Usage: %s num\n", argv[0]);
  66. exit(-1);
  67. }
  68. num = atoi(argv[1]);
  69. printf("Please enter %d integers:\n", num);
  70. for (i = 0; i < num; i++) {
  71. tmp = malloc(sizeof(struct mytype));
  72. if (!tmp)
  73. perror("Allocate dynamic memory");
  74. scanf("%d", &tmp->num);
  75. ret = my_insert(&mytree, tmp);
  76. if (ret < 0) {
  77. fprintf(stderr, "The %d already exists.\n", tmp->num);
  78. free(tmp);
  79. }
  80. }
  81. printf("\nthe first test\n");
  82. print_rbtree(&mytree);
  83. my_delete(&mytree, 21);
  84. printf("\nthe second test\n");
  85. print_rbtree(&mytree);
  86. return 0;
  87. }

编译并执行:

[cpp] view plaincopy
  1. $ gcc rbtree.c test.c -o test
  2. richard@tanglinux:~/algorithm/redblack$ ./test 10
  3. Please enter 10 integers:
  4. 23
  5. 4
  6. 56
  7. 32
  8. 89
  9. 122
  10. 12
  11. 21
  12. 45
  13. 23
  14. The 23 already exists.
  15. the first test
  16. 4 12 21 23 32 45 56 89 122
  17. the second test
  18. 4 12 23 32 45 56 89 122

详解Linux内核红黑树算法的实现 http://blog.csdn.net/npy_lp/article/details/7420689相关推荐

  1. 详解Linux内核红黑树算法的实现

    复制太难看了,以后试试 博文地址:http://blog.csdn.net/npy_lp/article/details/7420689 

  2. 从最大似然到EM算法浅解 http://blog.csdn.net/zouxy09/article/details/8537620

    1. EM blog的举例就是group 然后就是每个group的function很有效地串联所学的知识,看到的论文,所有的思考,都是有一定的逻辑关系,如何逐渐develop你的想法,都是有一定的源头 ...

  3. Mongodb参数详解(参考:http://blog.csdn.net/freebird_lb/article/details/8229567)

    MongoDB配置参数详解: #对mongo实例来说,每个host允许链接的最大链接数,这些链接空闲时会放入池中,如果链接被耗尽,任何请求链接的操作会被阻塞等待链接可用,推荐配置10 connecti ...

  4. Linux 内核红黑树分析

    Android binder 内核实现是用红黑树的,理解红黑树我觉得是每一个Linux er的重中之重,感谢格子森同学的投稿,周末愉快. 内核版本为 linux4.2.1 本文主要从红黑树的代码实现入 ...

  5. 基于Linux内核红黑树的TR069参数解析工具:树形结构+CPE RPC支持

    目录 tr069_oid.h tr069_oid.c demo.c tr069_oid.h /* Dependencies -------------------------------------- ...

  6. 装配 Spring Bean 详解https://blog.csdn.net/kkfd1002/article/details/79936447

    装配 Bean 的概述 前面已经介绍了 Spring IoC 的理念和设计,这一篇文章将介绍的是如何将自己开发的 Bean 装配到 Spring IoC 容器中. 大部分场景下,我们都会使用 Appl ...

  7. .net反射详解 原文://http://blog.csdn.net/wenyan07/article/details/27882363

    概述反射 通过反射可以提供类型信息,从而使得我们开发人员在运行时能够利用这些信息构造和使用对象. 反射机制允许程序在执行过程中动态地添加各种功能. 运行时类型标识 运行时类型标识(RTTI),可以在程 ...

  8. Mysql分析-profile详解(转http://blog.csdn.net/ty_hf/article/details/54895026)

    一.前言 当我们要对某一条sql的性能进行分析时,可以使用它. Profiling是从 mysql5.0.3版本以后才开放的. 启动profile之后,所有 查询包括错误的语句都会记录在内. 关闭会话 ...

  9. MMX指令集(详解)https://blog.csdn.net/dahan_wangtao/article/details/1944153

    EMMS MMX状态置空: 将FP特征字置空(全1),使后续浮点指令可以使用浮点寄存器,其他MMX指令自动置FP为全0.本指令应在所有MMX例程结束和调用可含有FP指令的例程时使用,以清除MMX状态. ...

  10. linux下各种格式软件的安装(引用http://blog.csdn.net/zyz511919766/article/details/7574040)

    首先介绍两个简单的方式 第一:sudo apt-get install packagename 命令 如果我们知道我们要安装的软件的确切的名称,那么我们可以简单的通过此条命令来获取和安装软件.apt- ...

最新文章

  1. ADO.NET并发性
  2. Win7下U盘安装Ubuntu14.04双系统
  3. 自定义工作流任务控件
  4. 【Vue.js】vue用户登录功能
  5. web.xml隐藏html,web.xml
  6. Vue + Bootstrap|Element UI——模态框被遮罩层遮盖问题解决方案
  7. Qt Creator分析QML应用程序
  8. 移动研发 DevOps 落地实践
  9. Map Reduce和流处理
  10. 最流行的 IDE 之争:Eclipse 反超 Visual Studio 成第一
  11. kubernets 集群和本地环境联调环境打通工具kt-connect
  12. 数学物理方法pdf_3位物理学家意外发现基础数学新方法,数学天才陶哲轩:我开始压根不相信...
  13. python实现网页微信登陆_(转帖)网站微信登录-python 实现
  14. 定义客户类(Customer): 1,客户类的属性包括:姓名、年龄、电话、金钱数量、账号、密码; 2,方法包括:购买商品、付款、显示自己的信息。 3,创建测试类,在main方法中使用客户类创建两个客户
  15. 终于!这个速算题自动批改程序被我给做出来了!
  16. 前端开发入门 --摘自慕克网大漠穷秋
  17. 戴尔易安信引领科技创新,以全面的端到端解决方案助力企业“数”造未来
  18. easyUI日期框的日期范围限制
  19. ui设计是干啥的: ui设计师主要是做什么的呢
  20. 图像识别(二)| 图像的色彩空间

热门文章

  1. ios客户端发现_华为爱奇艺手机活动开发总结
  2. 数据大屏之跑马灯功能
  3. 云课堂 php代码,基于ThinkPHP二开高仿网易云课堂整站PHP源代码
  4. 激光粒度仪测试原理及详情解答【注解】
  5. IDCC2018|上海数据港股份有限公司副总裁、数据中心首席架构师王海峰:标杆管理驱动数据中心建设变革...
  6. Labwindows/CVI 编写CAN通讯的上位机
  7. 多多自走棋服务器不稳定,《多多自走棋》将登陆Epic平台 PC版数据将与移动版互通...
  8. 人工智能粒子群优化和群智能
  9. maven运行Error:(3, 14) java: 程序包不存在
  10. 光速不变原理引发的诡异现象与时空分量的解释