1、红黑树的应用
Map、Nginx、定时器、内存管理
2、红黑树的五大特性
- 每个节点要么是红色,要么是黑色;
- 根节点永远是黑色的;
- 所有的叶节点都是是黑色的(注意这里说叶子节点其实是 NIL 节点,被隐藏的);
- 每个红色节点的两个子节点一定都是黑色;
- 从任一节点到其子树中每个叶子节点的路径都包含相同数量的黑色节点;
将下述数组中的Key依次插入就生成了下图的平衡二叉树
int keyArray[20] = {24,25,13,35,23,26,67,47,38,98, 20,19,17,49,12, 21,9,18,14,15};
3、红黑树的旋转
红黑树的旋转–需要调整三个方向,旋转时暂时先不考虑颜色
3.1 左旋–针对X节点
1、X的右节点指向Y的左节点(b),Y的左节点(b)的父亲变为x
2、Y的父亲指向X的父亲,X的父亲的左/右孩子变为Y
3、Y的左子树变为X,X的父亲变为Y
对应代码如下
void rbtree_left_rotate(rbtree *T, rbtree_node *x) {
rbtree_node *y = x->right; // x --> y , y --> x, right --> left, left --> right
x->right = y->left; //1 1
if (y->left != T->nil) { //1 2
y->left->parent = x;
}
y->parent = x->parent; //1 3
if (x->parent == T->nil) { //1 4
T->root = y;
} else if (x == x->parent->left) {
x->parent->left = y;
} else {
x->parent->right = y;
}
y->left = x; //1 5
x->parent = y; //1 6
}
3.2 右旋–针对Y节点
1、Y的左子树指向X的右子树,X的右子树的父亲变为Y
2、X的父亲变为Y的父亲
3、Y变为X的右子树
代码如下–就是将左旋的代码Y改为X,X改为Y,left改为right,right改为left
void rbtree_right_rotate(rbtree *T, rbtree_node *y) {
rbtree_node *x = y->left;
y->left = x->right;
if (x->right != T->nil) {
x->right->parent = y;
}
x->parent = y->parent;
if (y->parent == T->nil) {
T->root = x;
} else if (y == y->parent->right) {
y->parent->right = x;
} else {
y->parent->left = x;
}
x->right = y;
y->parent = x;
}
4、红黑树插入
前提–插入时已经是一颗满足条件的红黑树
由于插入前黑高已经固定,所以我们插入的肯定是一个红色节点(不能打破性质5),所以可以推断出如下4种插入情况
(1)新节点位于根节点,其没有父节点,处理思路:将该节点直接设为黑色即可
(2)新节点的父节点已然是黑色,处理思路:不用动,这已然是一颗红黑树,直接插入 (key=98节点)
(3)父节点和叔节点都是红色,
处理思路:a.将父节点和叔节点设为黑色;b.将祖父节点设为红色;c.将祖父节点设为当前节点,并继续对新当前节点进行操作,直到此树平衡(添加节点key=19)
对应代码如下
//y是要插入节点z的叔叔节点
rbtree_node *y = z->parent->parent->right;
if (y->color == RED)//父节点红色,叔节点也是红色
{
z->parent->color = BLACK; //父节点设为黑色
y->color = BLACK; //叔节点设为黑色
z->parent->parent->color = RED;//祖父节点设为红色
z = z->parent->parent; //祖父节点设为当前节点,继续操作
}
(4)父节点是红色,叔节点是黑色,又分如下四种情况:
a.当前节点是父亲的左孩子,父亲是祖父的左孩子(Left-Left),
处理思路:1.将祖父节点右旋;2.交换父节点和祖父节点的颜色
注意:下图的17不是新插入的,此时正在调整过程中
b.当前节点是父亲的右孩子,父亲是祖父的左孩子(Right-Left),
处理思路:1.将父节点左旋,并将父节点作为当前节点; 2.然后再使用Left Left情形
c.当前节点是父亲的右孩子,父亲是祖父的右孩子(Right-Right)key=35是新插入的节点
处理思路:1.将祖父节点左旋;2.交换父节点和祖父节点的颜色
d.当前节点是父亲的左孩子,父亲是祖父的右孩子(Left-Right),key=26是新插入的节点
处理思路:1.将父节点右旋,并将父节点作为当前节点(此时35作为新插入的节点);
2.然后再使用Right Right情形