linux学习22,linux内核中的红黑树是怎样设计和使用C语言实现的?
发表于: 2019-01-30 21:17:23 | 已被阅读: 21 | 分类于: Linux笔记
上一节较为详细的讨论了普通二叉搜索树的局限性,在此基础上引出了
为什么红黑树插入或者删除节点时,要重新调整树?
在回答这个问题之前,先来回顾一下红黑树的 5 条规则,要想保持一棵树始终是红黑树,就必须遵守这 5 条规则。
- 所有节点要么是红色,要么是黑色
- 根节点必须是黑色
- 所有叶子(NULL,or NIL)节点都是黑色的
- 红色节点的两个子节点必须是黑色(不能有连续的红色节点)
- 从根节点到任意叶子节点,黑色节点的数目是相等的
现在设想一下,如果往树中插入的节点是红色的,并且它的父节点也是红色的,那不就与
因此,在往红黑树中插入新节点,或者从中删除节点时,非常有必要仔细检查一下是否需要对树做
先来看看往红黑树中插入新节点的情况
首先应该清楚,红黑树仍然是一棵
普通二叉搜索树节点的插入和删除操作,第 20 节文章已经介绍的比较清楚,感到陌生的朋友可以过去看看。
插入新节点后,首先要为新节点染色(规则1)。Linux 内核默认新节点是红色,为什么不是黑色呢?这是因为新节点是红色时,需要考虑的情况比较少,较容易作调整。
我们假设插入的节点不是根节点,那么新插入的红色节点显然不会违反红黑树的规则1、规则2、规则3、规则5,唯一可能会违反的只有规则4。
那么,如何作调整,才能确保插入新节点的二叉树仍然是一棵红黑树呢?来看看 Linux 内核源代码是怎么实现的吧。内核的红黑树时用到的数据结构的 C语言代码如下,请看:
100 struct rb_node
- 101 {
| 102 unsigned long rb_parent_color;
| 103 #define RB_RED 0
| 104 #define RB_BLACK 1
| 105 struct rb_node *rb_right;
| 106 struct rb_node *rb_left;
| 107 } __attribute__((aligned(sizeof(long))));
108 /* The alignment might seem pointless, but allegedly CRIS needs it */
109
110 struct rb_root
- 111 {
| 112 struct rb_node *rb_node;
| 113 };
116 #define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3))
117 #define rb_color(r) ((r)->rb_parent_color & 1)
118 #define rb_is_red(r) (!rb_color(r))
119 #define rb_is_black(r) rb_color(r)
- 120 #define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0)
- 121 #define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0)
当 U 是红色的时候,如下图(白色表示不关心节点的颜色):
继续分析,如果 U 是黑色并且 N 是 P 的右儿子,如下图:
关于“左旋转”和“右旋转”可参考上一节的文章。
需要说明的是,这些操作步骤都是递归的,解决问题的关键思路就是先解决局部问题,让局部称为一棵红黑树,把违反规则的部分逐层往上推,一直推到根节点。到根节点就好办了,直接把它染成黑色即可。
从红黑树删除一个节点,Linux 内核如何做调整,以继续维持一棵红黑树呢?
要从红黑树删除节点,首先仍然是把它当做一棵普通的二叉搜索树。在第 20 节我们详细讨论过,如果要删除的节点至多只有一个子树,那么直接把它的子树挂到它的父节点上就可以了。
如果要删除的节点有两个子树呢?Linux 内核采取的方法和第 20 节介绍的方法是类似的:都是选择一个
这就清楚了,当从红黑树中删除节点时,Linux 内核采取的方法巧妙的保证了
如果 N 是黑色,那么它显然很可能至少会违反红黑树的
如果 O 是红色的,那么 P 必定是黑色的,如下图:
如果 O 的两个儿子都是黑色,如下图:
否则,如果 O 的右儿子是黑色,左儿子时红色,如下图:
总结
至此,我们就分析完 Linux 内核关于红黑树调整部分的源代码了。可以看出,维护一棵红黑树其实并不是特别难,往树中插入新节点,或者从树中删除节点,完全可以先把其当作是一棵普通的二叉搜索树,之后再重新调整就可以了。这一过程是不是很像魔方?插入和删除操作可能会将魔方打乱,不过之后再变换,就又能把魔方还原了。