红黑树用在哪里?
如何使用红黑树?
使用红黑树的2中情况:
1. key, value ---> 重在红黑树的查找
2. 利用红黑树的中序遍历, 终须遍历是顺序的。查找速度快。
CFS: 从红黑树的叶子节点开始查找, 速度快
内存管理:malloc分配的内存使用红黑树来管理的,块内存如何表述(key <-> value)一块内存 :
- 开始位置 + 长度
- 开始位置 + 结束位置
内存开始的位置保存在key,用一个指针指向开始的位置,在加上一个内存的长度,就可以表述一块内存;每分片一块内存就想红黑树中添加一个节点,如此就形成了一个内存的红黑树。
红黑树的性质:
- 每个节点是红色的或者黑色的
- 根节点是黑色的
- 每个叶子节点是黑色的
- 如果一个节点是红色的,则它的连个儿子都是黑色的
- 对每个节点,从该节点到其子孙节点是所有路径上具有包含相同数目的黑节点。(红黑树平衡的是黑色的节点个数)
左右字数的高度相差: 2n-1
红黑树的左右旋转,借用网络图:
红黑树的插入:
1. 红黑树为空树:节点直接插入,因为根节点为黑色,所以节点颜色变为黑色。
2. 插入节点的key已经存在:
- 把插入节点的颜色设置为要替代节点的颜色;
- 更新要替代节点的key为要插入节点的key;
3. 插入节点的父节点的颜色为黑色: 直接插入该节点;
4. 插入节点的父节点的颜色为红色:
- 插入节点的叔叔节点存在且为红色:父亲、叔叔结点变黑,祖父结点变红;插入节点执行其祖父结点;
- 叔叔结点不存在或为黑结点:如果叔叔节点非红,即为叶子节点。因为如果叔叔节点为黑色,而父亲节点为红色,那么叔叔节点所在子树的黑色节点个数就比父亲节点所在子树的黑色节点的个数多了,所以就不满足上述红黑树性质5。
- 并且插入结点的父亲结点是祖父结点的左子结点
- 如果插入结点是其父结点的左子结点:进行右旋;
- 如果插入结点是其父结点的右子结点:先左旋再右旋;
- 并且插入结点的父亲结点是祖父结点的右子结点
- 如果插入结点是其父结点的左子结点:先右旋在左旋;
- 如果插入结点是其父结点的右子结点:进行左旋;
- 并且插入结点的父亲结点是祖父结点的左子结点
上面的插入过程写的有点多,不好理解, 总结一下吧(新插入结点为N):
1. 树空:N为根且变黑;
2. 父黑:直接插
3. 父红叔红:父、叔变黑祖变红,祖为新的平衡结点从新递归;
4. 父红叔空:N、父同边扭扭完事(同左右旋、通右左旋,然后涂色);N、父不同边,先扭同边在旋转。
摘个网友的举例图:
对应代码如下:
typedef int KEY_TYPE;
#define RED 0
#define BLOCK 1
int key_compare(KEY_TYPE a, KEY_TYPE b)
{
return 0;
}
typedef struct _rbtree_node {
unsigned int color;
struct _rbtree_node *left;
strcut _rbtree_node *right;
struct _rbtree_node *parent;
KEY_TYPE key;
void *value;
} rbtree_node;
typedef struct _rbtree {
rbtree_node *root;
rbtree_node *nil;//leaf node
} rbtree;
//
void _left_rotate(rbtree *T, rbtree_node *x)
{
rbtree_node *y = x->right;
//1
x->right = y->left;
if (y->left != T->nil) {
y->left->parent = x;
}
//2
y->parent = x->parent;
if (x->parent == T->nil) {
T->root = y;
} else if (x == x->parent->left) {
x->parent->left = y;
} else {
x->parent->right = y;
}
//3
y->left = x;
x->parent = y;
return ;
}
//
void _right_rotate(rbtree *T, rbtree_node *y)
{
rbtree_node *x = y->left;
//1
y->left = x->right;
if (x->right != T->nil) {
x->right->parent = y;
}
//2
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;
}
//3
x->right = y;
y->parent = x;
return ;
}
//首先插入的都是叶子结点,即z插入后是叶子结点,插入后再进行旋转
void rbtree_insert_fixup(rbtree *T, rbtree_node *z)
{
while (z->parent->color == RED) {
//判断父节点是祖父节点的左子树还是右子树
if (z->parent == z->parent->parent->left) {
rbtree_node *y = z->parent->parent->right;
if (y->color == RED) {
z->parent->color = BLOCK;
y->color = BLOCK;
z->parent->parent->color = RED;
z = z->parent->parent;
} else {
if (z == z->parent->right) {
//插入节点的key大于其父节点的key
z = z->parent;
_left_rotate(T, z);
}
z->parent->color = BLOCK;
z->parent->parent->color = RED;
_right_rotate(T, z);
}
} else {
rbtree_node *y = z->parent->parent->left;
if (y->color == RED) {
z->parent->color = BLOCK;
y->color = BLOCK;
z->parent->parent->color = RED;
z = z->parent->parent;
} else {
if (z == z->parent->left) {
z = z->parent;
_right_rotate(T, z);
}
z->parent->color = BLOCK;
z->parent->parent->color = RED;
_left_rotate(T, z);
}
}
}
T->root->color = BLOCK;
return ;
}
红黑树的删除:
转一下这位老兄的文章: