摘要

兹博文探讨四个重点:1、简单介绍红黑树;2、红黑树节点数据结构;3、红黑树节点中父节点指针域和自身节点颜色有机结合;4、定义红黑树和操作树节点父节点指针和节点颜色的接口,包括一系列宏和两个函数。

注:所有代码源自kernel 3.10

前言

援引<Documentation/rbtree.txt>中的一部分,并做简单的翻译(个别地方不太好),姑且作为红黑树博文的开篇。

What are red-black trees, and what are they for?
------------------------------------------------
什么是红黑树?并且他们用于什么? Red-black trees are a type of self-balancing binary search tree, used for
storing sortable key/value data pairs. This differs from radix trees (which
are used to efficiently store sparse arrays and thus use long integer indexes
to insert/access/delete nodes) and hash tables (which are not kept sorted to
be easily traversed in order, and must be tuned for a specific size and
hash function where rbtrees scale gracefully storing arbitrary keys). 红黑树是一种自平衡二叉搜索树,用于存储可排序的键/数值数据。它不同于基数树
(用来有效地存储稀疏数组,因此使用长整数索来插入/存取/删除节点)和哈希表
(它不用进行排序就可以很容易地按序遍历,但必须设定具体大小和散列函数,而红黑树优
雅的扩展存储任意键) Red-black trees are similar to AVL trees, but provide faster real-time bounded
worst case performance for insertion and deletion (at most two rotations and
three rotations, respectively, to balance the tree), with slightly slower
(but still O(log n)) lookup time. 红黑树类似于AVL树,但对于插入(至多两次旋转)和删除(至多三次旋转来重新平衡红黑树)
提供最坏情况下实时复杂度的更快的性能,和稍慢的查询时间(依旧是O(log n)) To quote Linux Weekly News: There are a number of red-black trees in use in the kernel.
The deadline and CFQ I/O schedulers employ rbtrees to
track requests; the packet CD/DVD driver does the same.
The high-resolution timer code uses an rbtree to organize outstanding
timer requests. The ext3 filesystem tracks directory entries in a
red-black tree. Virtual memory areas (VMAs) are tracked with red-black
trees, as are epoll file descriptors, cryptographic keys, and network
packets in the "hierarchical token bucket" scheduler. 援引Linux Weekly News:
deadline和CFQ(Completely Fair Queueing)两种I/O调度,运用rbtree跟踪请求;
CD/DVD块数据驱动器也是如此。高分辨率定时器代码中使用rbtree出色的组织定时器的请求。
Ext3文件系统使用rbtree跟踪目录项。虚拟存储区(VMA)的跟踪,以及epoll的文件描述符,
加密密钥,“分层令牌桶”调度网络数据包也均有rbtree的使用。

1、红黑树节点数据结构

<linux/rbtree.h>

struct rb_node {
unsignedlong __rb_parent_color; //下面详细解析这个变量
structrb_node *rb_right;//右孩子指针域
structrb_node *rb_left;//左孩子指针域
} __attribute__((aligned(sizeof(long))));

——sizeof(long)返回long类型的字节数n;aligned(n)——指明结构体n字节对齐;__attribute__是GCC编译器的扩展,属性约束!这句话的含义:struct rb_node按照4字节(long)对齐!对于__attribute__和aligned详细说明和用法可以查阅gcc在线用户手册

structrb_node结构体4字节对齐,那么它在内存中的基地址base一定是的4的倍数(base % 4 == 0);由于指针在32位系统之上占用4B,则32bit基地址base的低两位一定为0(否则“base % 4 == 0”不成立,不再是4B对齐)。

红黑树节点必须保存四个值:

(1)自身的颜色(color,红黑树的必须)

(2)父节点指针(使得红黑树向上走查找父节点的时间复杂度是O(1))

(3)左孩子节点指针(使得红黑树向左走查找左孩子的时间复杂度是O(1))

(4)右孩子节点指针(使得红黑树向右走查找右孩子的时间复杂度是O(1))

<linux/augmented.h>
#define RB_RED 0
#define RB_BLACK 1

红黑树节点的颜色只有两个——红(0)和黑(1),使用1bit就足够表示!而struct rb_node结构体基地址低2位“不使用”,那么可以使用最低位来存储节点的颜色,把基地址和颜色值或(|)运算整合成一个变量__rb_parent_color,这样就节省了一个存储颜色的变量。

【如果单独定义一个变量来存储颜色值,假设4B无符号整型(即使是shot或者char类型,在4B对齐状态下也是占用4B)那么当树的节点数量增长时,每增长一个树节点,就会多开销4B!并且存储父节点地址的变量低两位又不使用,4个树节点就浪费1B;记住:核心内存有限,每消耗1B,核心就少1B——不损失性能的情况下,能节约就节约!】

2、红黑树根

<linux/rbtree.h>
struct rb_root {
struct rb_node *rb_node;
};

通过此指针是否为空,来判断树是否为空!引入下面接口

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

定义一棵红黑树,初始化为空树:

static struct rb_root mytree = RB_ROOT;

其中RB_ROOT在<linux/rbtree.h>中定义

#define RB_ROOT	(struct rb_root) { NULL, }

它将{NULL,}强制转换成(struct rb_root),初始化树为空!

3、操纵成员__rb_parent_color的接口

3.1、提取父节点地址

<linux/rbtree.h>
#define rb_parent(r) ((struct rb_node *)((r)->__rb_parent_color & ~3)) <linux/rbtree_augmented.h>
#define __rb_parent(pc) ((struct rb_node *)(pc & ~3))

32bit

3=0000 0000 0000 0000 0000 0000 0000 0011

~3 =1111 1111 1111 1111 1111 1111 1111 1100

__rb_parent_color & ~3=过滤掉低2位(非0变成0),保留第3-32位。由于__rb_parent_color是无符号整数,强制转换成struct rb_node *类型。

上面两个宏作用一样,不过参数不一样——前者是指向节点的指针r;后者是节点成员unsignedlong  __rb_parent_color。此外,”__”前缀也强调内外之分。

<lib/rbtree.c>
static inline struct rb_node *rb_red_parent(struct rb_node *red)
{
return (struct rb_node *)red->__rb_parent_color;
}

由于红色节点的成员变量__rb_parent_color的低两位为0,因此__rb_parent_color的值就是此红色节点的父节点基址。转换下数据类型,直接返回即可!

3.2、提取和判断结点颜色

<linux/rbtree_augmented.h>
#define __rb_color(pc) ((pc) & 1) //struct rb_node成员“pc”值的最低位。
#define __rb_is_black(pc) __rb_color(pc)
//逻辑判断节点是不是黑色;pc值最低位为1,节点是黑色(真);最低位为0,不是黑色(假)
#define __rb_is_red(pc) (!__rb_color(pc))
///逻辑判断节点是不是红色;pc值最低位为1,不是红色(假);最低位为0,是红色(真)
#define rb_color(rb) __rb_color((rb)->__rb_parent_color)
//提取节点颜色;值0红色,值1为黑色;
#define rb_is_red(rb) __rb_is_red((rb)->__rb_parent_color)
//封装成外部接口,接受struct rb_node* 类型rb为参数
#define rb_is_black(rb) __rb_is_black((rb)->__rb_parent_color)
//封装成外部接口,接受struct rb_node* 类型rb为参数

3.3、设置父节点指针

<linux/rbtree_augmented.h>
static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p)
{
rb->__rb_parent_color = rb_color(rb) | (unsigned long)p;
}

由图示可以很容易明白,这个函数可以用来更新节点的父节点指针使得其指向新的父节点。

3.4、设置父节点指针和节点颜色

<linux/rbtree_augmented.h>
static inline void rb_set_parent_color(struct rb_node *rb, struct rb_node *p, int color)
{
rb->__rb_parent_color = (unsigned long)p | color;
}

<lib/rbtree.c>
static inline void rb_set_black(struct rb_node *rb)
{
rb->__rb_parent_color |= RB_BLACK;
}

3.5、节点的逻辑判断

<linux/rbtree.h>
/* 'empty' nodes are nodes that are known not to be inserted in an rbree */
注释已经说得够明白了,代码也很明白。原因以后再论。
#define RB_EMPTY_NODE(node) \
((node)->__rb_parent_color == (unsigned long)(node))
#define RB_CLEAR_NODE(node) \
((node)->__rb_parent_color = (unsigned long)(node))

预告:

对于红黑树的操作,下篇博文给予详细解析!

声明:

>>     知识要传播,劳动要尊重! 受益于开源,回馈于社会! 大家共参与,服务全人类!

>>     本博文由my_live_123原创(http://blog.csdn.net/cwcmcw),转载请注明出处!   ^_^

Linux红黑树(一)——数据结构的更多相关文章

  1. cb22a_c++_标准模板库_STL_map_multimap红黑树(数据结构)关联容器

    cb22a_c++_标准模板库_STL_map_multimap红黑树(数据结构)关联容器map(映射,key不能重复,一对一对的,value_type(1, "one")),mu ...

  2. Linux红黑树(二)——访问节点

    核心对红黑树使用两点说明 1.头文件 <Documentation/rbtree.txt> Linux's rbtree implementation lives in the file ...

  3. 安排:《蚂蚁花呗1234面:Redis+分布式架构+MySQL+linux+红黑树》

    前言: 大厂面试机会难得,为了提高面试通关率,建议朋友们在面试前先复盘自己的知识栈,依据掌握程度划分重要.优先级,系统地去学习!如果不准备充分就去参加面试,既会失去进入大厂的机会,更是对自己的不负责. ...

  4. 浅谈算法和数据结构: 七 二叉查找树 八 平衡查找树之2-3树 九 平衡查找树之红黑树 十 平衡查找树之B树

    http://www.cnblogs.com/yangecnu/p/Introduce-Binary-Search-Tree.html 前文介绍了符号表的两种实现,无序链表和有序数组,无序链表在插入的 ...

  5. 数据结构与算法(十):红黑树与TreeMap详细解析

    本文目录 一.为什么要创建红黑树这种数据结构 在上篇我们了解了AVL树,既然已经有了AVL这种平衡的二叉排序树,为什么还要有红黑树呢? AVL树通过定义我们知道要求树中每一个结点的左右子树高度差的绝对 ...

  6. java数据结构和算法06(红黑树)

    这一篇我们来看看红黑树,首先说一下我啃红黑树的一点想法,刚开始的时候比较蒙,what?这到底是什么鬼啊?还有这种操作?有好久的时间我都缓不过来,直到我玩了两把王者之后回头一看,好像有点儿意思,所以有的 ...

  7. JDK1.8的HashMap数据结构及红黑树

    在JDK1.6,1.7中,HashMap的实现都是用基础的“拉链法”去实现,即数组+链表的形式.如下图:通过不同的hash值,来对数据进行分配存储. 关于HashMap的Entry长度,可以参考htt ...

  8. 剑指XX游戏(六) - 轻松搞定面试中的红黑树问题

    原文地址 http://blog.csdn.net/silangquan/article/details/18655795?utm_source=tuicool&utm_medium=refe ...

  9. 研究jdk关于TreeMap 红黑树算法实现

    因为TreeMap的实现方式是用红黑树这种数据结构进行存储的,所以呢我主要通过分析红黑树的实现在看待TreeMap,侧重点也在于如何实现红黑树,因为网上已经有非常都的关于红黑树的实现.我也看了些,但是 ...

随机推荐

  1. windows下配置两个或多个Tomcat启动的方法

    确保window的环境变量中找不到CATALINA_HOME和CATALINA_BASE 修改server.xml,用解压版的tomcat,不要用安装版的. 1.修改http访问端口 conf下的se ...

  2. memcache和memcached

    一:Memcached.memcached.memcache. 其中首字母大写的Memcached,指的是Memcached服务器,就是独立运行Memcached的后台服务器,用于存储数据的“数据库” ...

  3. 关于HttpServlet和Servlet以及doPost和doGet关系

    这两天在看Servlet和Jsp,spring太难了,还是先看看基础,只怪自己太弱了. Servlet是一个接口,本身定义的是一种网络服务,HttpServlet是已经实现了Servlet接口,也就是 ...

  4. 2015-09-28 Javascript

    1.Javascript是什么? JavaScript是一种脚本语言,结构简单,使用方便,其代码可以直接放入HTML文档中,可以直接在支持JavaScript的浏览器中运行.JavaSript. Ja ...

  5. git some cookies

    *首先得配置和本地的操作实际上都很简单,忽略了:*git 添加远程仓库 git remote add 仓库名 url:*先pull下来,语法 git pull 远程仓库名 远程分支名[:当地分支名], ...

  6. SpringBoot入门系列:第一篇 Hello World

    跟随SpringBoot的文档(http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-d ...

  7. ESXi控制台TSM:弥补vSphere Client不足

    当vSphere Client不能完成某些任务时,主机的ESXi控制台及其技术支持模式(TSM)可能能派上用场. ESXi控制台允许管理员执行不能通过vSphere Client进行配置的管理任务,比 ...

  8. Carthage&&cocopads 摘抄笔记

    Carthage 是 iOS/Mac 开发生态圈的一个包管理工具,与现在流行的 CocoaPods 不同,它是一个去中心化的解决方案.知道它已经有一段时间了,但是一直没有好好玩过,今天整合 Carth ...

  9. php中函数不确定参数个数时获取所有参数

    func_get_arg(),func_get_args()和func_num_args()的用法 这三个函数呢,都是以func_打头的,顾名思义,是跟php的函数有关的.我们在写类的时候也经常用到这 ...

  10. asp.net能不托管吗?

    弱弱地问一句,整个部署在IIS中的asp.net项目能不托管吗? 或者说有没有用纯粹的非托管语言(比方说C语言)写的非托管asp.net项目?