随处可见的红黑树
红黑树性质
- 每个节点不是红色就是黑色的
2. 根节点是黑色的
3. 每个叶子节点是黑色的
2 根和叶子节点都是黑色的 - 如果一个结点是红色的,则他的两个根节点都是黑色的
(没有连续的两个红色父子结点) - 对于每个节点,从该节点到其子孙结点的所有路径上都包含相同数目的黑节点
(对于每个节点,黑高都相同)
黑高 用来判断 是否是红黑树
红黑树的应用场景
- Linux进程调度 CFS
- Nginx Timer 事件管理
- Epoll 事件块的管理
红黑树的数据证明与推导
WHY
- 1 红黑树为什么要进行左旋 右旋 变色呢?
在插入,删除操作的时候,会破坏红黑树的自平衡,为了实现红黑树的自平衡 - 2
- 3
- 4
手撕红黑树的左旋与右旋
思路:左旋涉及到 x的父节点 x y b 共四个结点的交互,具体来说:
- x的右节点指向b
- b的父节点指向x
- y的父节点指向x的父节点
- x的父节点的左(或右)结点指向y
- x的父节点指向y
- y的左节点指向x
左旋
void retree_left_rotate(rbtree *T, rebtree_node *x)
{
rbtree_node *y = x->right;
x->right = y->left;
if (y->left != T->nil){
y->left->parent = x;
}
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;
}
x->parent = y;
y->left = x;
}
右旋
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;
}
红黑树插入的实现与添加三种情况的证明
- 查找插入的位置
插入结点后,第一个问题,设置当前结点颜色为红色?黑色?
红色,因为如果为黑色,则一定破坏左右结点黑高一致的原则,如果为红色,则可能破坏父子结点为
红色的原则,所以要进行下一步插入后的自平衡操作 - 插入后自平衡
1.无需进行 变色,左旋,右旋
2.变色 :若 插入结点的父节点为红色(可以推到父节点一定为黑色),叔父结点为红色,需要变色—>父节点,叔父结点 与 祖父结点 颜色变化【这也是唯一一种会增加红黑树黑色结点层数(因为默认插入的结点颜色是红色,左旋,右旋也不会改变黑色结点层数)的插入情景】
3.左旋,右旋:若 插入结点的父结点为红色,叔父结点不存在(为黑色(叶子结点)),此时因为左右结点层数不一致,这个时候应该想到左旋,右旋操作 + 变色 (TODO)
红黑树的线程安全做法
- 对每个正在操作的结点加锁
分析红黑树工程实用的特点
红黑树等数据结构的插入删除
思考
-
1 黑结点可以同时包含一个红子结点和一个黑子结点吗?
可以,如下所示
-
2
-
3
案例分析
案例一、服务器端高并发IO的keep alilve方案,满足一下几个需求
- 每个IO都是自己的时间戳
- 每个IO收到自己的beat后,重置自己的定时器
- 若IO定时没有收到beat,则执行IO的回调函数,并重置定时器
- 若再次没有收到beat,销毁IO,注销定时器。
案例二、设计一个线程或者进程的运行体R与运行体调度器S的结构体
- 运行体R:包含运行状态{新建,准备,挂起{IO等待读, IO等待写, 睡眠,延时}, 退出},运行体 回调函数,回调参数
- 调度器S:包含栈指针,栈大小,当前运行体
- 调度器S:包含执行集合{就绪,延时,睡眠,等待}