文章目录
【参考】:
维基百科
【喵的算法课】红黑树 删除【12期】
1 红黑树演变
演变:线性查找(性能低O(n))—>二分查找((logn) 二查叉树会出现退化成链表的问题)
—>出现AVL平衡二叉树(数据变化有频繁更新节点问题)—>出现红黑树O(logn);
可见黑红树是在平衡二叉树的基础上,在数据的插入和删除上有所提升;
2 红黑树简介
红黑树是一棵近似平衡二叉搜索树,其中序遍历单调不减,并且能确保任何一个结点的左右子树的高度差小于两倍;
恢复红黑树的性质需要少量的颜色变更和不超过三次树旋转(对于插入操作是两次);
3 红黑树的性质
【1】结点是红或黑色;
【2】根节点是黑色;
【3】所有的叶子结点都是黑色;
【4】每个红色结点的两个子结点都是黑色,且一条路径上红色结点不能连续;
【5】从任一结点其每个叶子的所有路径都包含相同数目的黑色结点;
【特性】从根到叶子的最长的可能路径不多于最短的可能路径的两倍长;
4 红黑树操作
4.1 在插入和删除的注意事项
- 性质1和性质3总是保持着;
- 性质4只在增加红色节点、重绘黑色节点为红色,或做旋转时受到威胁;
- 性质5只在增加黑色节点、重绘红色节点为黑色,或做旋转时受到威胁;
4.2 基础知识
4.2.1 父结点以及叔结点
node* grandparent (node *n){
return n->parent->parent;
}
node* uncle(node *n){
if(n->parent == grandparent(n)->left)
return grandparent(n)->right;
else
return grandparent(n)->left;
}
4.2.2 左右旋转
4.2.2 几种删除情况
情况1
情况2
情况3
若删除B,则找到相应的前驱结点(左子树中最大的结点)或后继结点(右子树中最小的结点)来代替;
4.3 插入
- 一般插入将该结点设置为红色,由于最差的情况是导致两个红色冲突,而只需要对其进行旋转或颜色调整即可;若使用
黑色,则该路径上会多出一个黑色结点,比较难调整;
【插入操作】: p为被插入结点
【1】若p是根结点:直接将该结点设为黑色(注:此时该树为空树,插入的结点即为根结点);
【2】若p的父节点为黑色:不需要处理;
【3】若p的父节点是红色:
- 【3】若p的叔节点为红色;
- 将父节点设为黑色;
- 将叔叔结点设为黑色;
- 将祖父结点设为红色;
- 此时将祖父结点设为当前结点去递归从情形1开始;
- 【4】若p的叔结点为黑色,且当前结点是其父节点的右孩子;
- 将父节点作为新的当前结点;
- 以新的当前结点为支点左旋;
- 在按照情形5继续处理;
- 【5】若p的叔结点为黑色,且当前结点时其父节点的左孩子;
- 将父节点设为黑色;
- 将祖父结点设为红色;
- 以祖父结点为支点进行右旋;
void insert_case1(node *n){
if(n->parent == NULL)
n->color = BLACK;
else
insert_case2 (n);
}
void insert_case2(node *n){
if(n->parent->color == BLACK)
return; /* 树仍旧有效*/
else
insert_case3 (n);
}
void insert_case3(node *n){
if(uncle(n) != NULL && uncle (n)->color == RED) {
n->parent->color = BLACK;
uncle (n)->color = BLACK;
grandparent (n)->color = RED;
insert_case1(grandparent(n));
}
else
insert_case4 (n);
}
void insert_case4(node *n){
if(n == n->parent->right && n->parent == grandparent(n)->left) {
rotate_left(n);
n = n->left;
} else if(n == n->parent->left && n->parent == grandparent(n)->right) {
rotate_right(n);
n = n->right;
}
insert_case5 (n);
}
void insert_case5(node *n){
n->parent->color = BLACK;
grandparent (n)->color = RED;
if(n == n->parent->left && n->parent == grandparent(n)->left) {
rotate_right(n->parent);
} else {
/* Here, n == n->parent->right && n->parent == grandparent (n)->right */
rotate_left(n->parent);
}
}
4.4 删除
【删除操作】:x删除节点,s为x的兄弟结点
【1】x为红直接删除;
【2】x为黑色,x为根节点直接删除;
【2.1】x有一个红色子节点:
用红色子节点来替代被删除的节点,然后染为黑色;
【2.2】x都为黑色结点:
【2.2.2】s结点为黑色:
1)s有红色子节点(借用兄弟子节点修复)
- 子节点可能左孩子(右旋)或右孩子(先左旋再右旋)、或左右孩子(选左孩子);
- 在删除x后,对x的父节点进行旋转,s染为红色,两个子节点染为黑色;
2)s没有红色子节点(父结点向下合并红与黑)
- 【若父节点为红色】:s染红,父节点染黑即可;
- 【若父节点为黑色】:s染红,把父节点进行递归;
【2.2.3】s为红色(转变为黑色处理)
父节点右旋,兄弟结点染黑,父节点染红,然后使用兄弟为黑色的方法进行修复;
5 与AVL树的比较
- 与红黑树相比,AVL树提供更快的查询,由于他是更加严格的平衡即读的性能会更好;
- 红黑树提供更快的插入和删除操作,因为AVL的旋转操作更多,红黑树会更少一点,由于红黑树是一个近似平衡二叉树;
- AVL要存的额外的信息(heght)更多,需要用到更多的内存附加在每个节点,而红黑树要的信息相对较少,只需要一个bit来
判断是红还是黑即可,对额外的空间消耗更小,在百万级的结点个数;
- 若在读操作比较多,写操作少的情况就使用AVL;