上面介绍了二叉搜索树以及avl树,这里介绍另外一种近似平衡的二叉树– 红黑树
它的每个节点都有5个属性: color、key、left、right、p
一棵红黑树通常拥有以下性质
- 每个节点或是红色,或是黑色的 (rb)
- 根节点是黑色的
- 每个叶子节点也是黑色的
- 如果一个节点是红色的,则它的两个子节点都是黑色的
- 对于每个节点,从该节点到所有后代叶节点的简单路径上,均包含相同数目的黑色节点
上图 1 是红黑树,其余均不是
它的插入删除以及访问的时间复杂度为 O(lgN)
定义,bh(x) (black height) 为黑高,即节点x
到期叶子节点的黑色节点的个数
证明:
一个n个内部节点的红黑树,它的高度最多为
首先证明: 以任一节点的x为根的红黑树最少含有 个节点
归纳法证明:
由性质5,可以看出:
若x的高度为0,即其为叶子节点(nil),此时有
若x的高度为1,则至少有
对于一个任意节点x
,它的左右节点的黑高为bh(x)
或者bh(x)-1
.即它的最少黑高为bh(x)-1
,则 以x为根的子树,至少含有
个节点数,
由性质4,可以看出
对于高度为h的红黑树,它的黑高至少为 ,所以
解之可🉐:
得证
调整节点
与avl树,类似,红黑树的插入或者删除,可能破坏其平衡(性质)。因此需要进行旋转调整。
平衡的破坏
我们设定每次插入的元素都是红色节点(这样子可以避免最坏情况的发生),这样子不会破坏性质1、3、5.
对于性质2的破坏,我们很容易进行识别出来。对于性质4的破坏,我们要进行简单分析一下。
对于更新破坏红黑树特性的问题,有一些调整的方法:
- BST operation
- Color changes
- Restructure the tree via rotation (recolor、lint)
左旋 and 右旋
这些操作只是进行了指针的交换,话费常数时间。
Insert element (O(lgN))
对于新插入的节点,我们设定它都是红色的,然后采用一些操作,对其平衡性进行调整
主要的调整方法为:
一个例子
先通过一个实例进行详细描述,后面在进行抽象,下面是一个红黑树插入元素15的过程。
- 先找到待插入的位置,插入元素15,并将其着色为 红色
- 15的祖父节点颜色是黑色,而它的父节点以及叔节点颜色是红色,因此,可以将祖父节点颜色下沉,即重新着色祖父节点为红色,叔节点以及父节点为黑色(case-1)。这时,冲突就变成了 10-18 颜色冲突
- 10-18 颜色冲突,且对于节点10来说,它并满足(case-1)的情况,无法进行重着色来满足条件4.此时,采用旋转操作,以节点18进行右旋(case-2),将10-18以及祖父节点在一条直线(看上去)。
4. 再以节点7为节点,进行左旋(case-3),然后重新着色(次数左子树黑高降低一,因此需要重新着色)
5. 将跟节点重新着色。
抽象
对于以上可以分为三种状态(或6种,因为左右情况对称,下面已左面情况为例)。
是插入的节点,`f(x)` 是其父节点,`f(f(x))` 是其祖父节点 `uncle of x as u(x)` 是其叔叔节点(y)。1 2 3 4 5 6 7 8 9 10 11 12
| 单线条是黑色节点,双线条是红色
##### case-1
![](https://raw.githubusercontent.com/hsjfans/git_resource/master/20190506202531.png)
该情况是,`f(f(x))` 是黑色,`f(x)`是红色,`u(x)`是红色。
此时,可以将其祖父节点(`f(f(x))`)颜色吓成,即变成
|
color[f(x)] = BLACK
color[f(f(x))] = RED
color[u(x)] = BLACK
1 2 3 4 5 6 7 8 9 10 11
| 此时,可以解决节点x的颜色冲突。
```c void re_color(RBTreeNode b) { b->parent->color = RED; b->parent->left->color = BLACK; b->parent->right->color = BLACK; return; }
|
case-2
此时,节点y
不是红色,无法通过着色来解决。x 是其父节点的右节点,视觉上看,节点C-A-B
不再一条直线上,此时采用旋转,来使节点C-A-B
在一条直线上
1 2 3 4 5 6 7
| a->right = c c->left = b b->parent = c a->parent= c->parent c->parent = a
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| void right_rotation(RBTree t, RBTreeNode a) { RBTreeNode b = a->parent; a->parent = b->parent; if (b->parent == NULL) { t->root = a; } else if (b->parent->left == b) { b->parent->left = a; } else { b->parent->right = a; } if (a->right != NULL) { a->right->parent = b; } b->left = a->right; b->parent = a; a->right = b; }
|
case-3
节点y
不是红色,无法通过着色来解决。x 是其父节点的左节点,视觉上看,节点C-A-B
在一条直线上。此时,可以采用旋转加重着色进行调整。
1 2 3 4 5 6 7 8 9 10 11
| c->left = a->right a->right = c a->parent=c->parent c->parent=a c->left=c
color[a] = BLACK color[c] = RED
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| void left_rotation(RBTree t, RBTreeNode a) { RBTreeNode b = a->right; a->right = b->left; if (a->left != NULL) { b->left->parent = a; } b->parent = a->parent; if (a->parent == NULL) { t->root = b; } else if (a == b->parent->left) { a->parent->left = b; } else { a->parent->right = b; }
b->left = a; a->parent = b; }
|
complicate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
| void insert(RBTree t, Element e, compare_func cmp) { RBTreeNode root = t->root; RBTreeNode temp = t->nil; while (root != t->nil) { temp = root; if (cmp(e, root->val) < 0) { root = root->left; } else { root = root->right; } }
RBTreeNode n = make_node(e); n->parent = temp; if (temp == t->nil) { t->root = n; } else if (cmp(e, temp->val) < 0) { temp->left = n; } else { temp->right = n; } n->left = t->nil; n->right = t->nil; n->color = RED; insert_fix_up(t, n); return; }
void insert_fix_up(RBTree t, RBTreeNode n) { while (n->parent->color == RED) { if (n->parent == n->parent->parent->left) {
RBTreeNode y = n->parent->parent->right; if (y->color == RED) { re_color(n->parent); n = n->parent->parent; } else if (n == n->parent->right) { n = n->parent; left_rotation(t, n); } else { n->parent->color = BLACK; n->parent->parent->color = RED; right_rotation(t, n->parent->parent); } } else { RBTreeNode y = n->parent->parent->left; if (y->color == RED) { re_color(n->parent); n = n->parent->parent; } else if (n == n->parent->left) { n = n->parent; right_rotation(t, n); } else { n->parent->color = BLACK; n->parent->parent->color = RED; left_rotation(t, n->parent->parent); } } }
t->root->color = BLACK; }
|
Deletion (O(lgN))
删除较插入操作更为复杂一些。
// todo
- skip_list
- avl_tree
- Treap
- RBTree and mysql
Reference
- geeksforgeeks
- r_b_tree