红黑树

简介

一直想写的一种数据结构,非常厉害的思想,插入,删除,查找,修改,都是\(log_2 n\)的时间复杂度。

比AVL更强大的是,插入删除综合效率比AVL要优秀一点。

性质

一颗红黑树是满足红黑性质的二叉搜索树:

  1. 每个节点是红色或者黑色的。
  2. 根节点是黑色的。
  3. 每个叶节点(NIL)是黑色的
  4. 如果一个节点是红色的,那么它的两个子节点都是黑色的。
  5. 对于每个节点,从当前节点到其所有后代叶节点的简单路径上,黑节点的个数是相同的。

插入

插入肯定是插入到某叶节点的位置,颜色设为红色。所以可能违反性质4,按一定的规则修复。

插入修复可能需要多次变色,但旋转最多2次。

共分为3种情况(是父节点的左右孩子,对称共8种情况,以本节点是父节点的左孩子为例):

  1. 叔节点为红色。
  2. 叔节点为黑色,且本节点为父节点的右孩子。
  3. 叔节点为黑色,且本节点为父节点的左孩子。

2和3情况需要旋转。情况1可能变为1,2,3。但2只能变为3,3完成修复,所以最多2次旋转。

删除

删除一定是非叶节点,在二叉搜索树的删除方法上做了一定改动,并且按一定规则修复。

删除修复可能需要多次变色,但旋转最多3次。

共分为4种情况(是父节点的左右孩子,对称共8种情况,以本节点是父节点的左孩子为例):

  1. 兄弟节点为红色,且左右孩子节点都为黑色。
  2. 兄弟节点为黑色,且左右孩子节点都为黑色。
  3. 兄弟节点为黑色,且左孩子节点为红色,右孩子节点为黑色。
  4. 兄弟节点为黑色,且左孩子节点为黑色,右孩子节点为红色。

实现

# include <cstdio>
# include <iostream>
using namespace std; /**************************/ /*
红黑树的定义:
1.每个结点要么是红色,要么是黑色。
2.根结点是黑色的。
3.每个叶结点(NIL)是黑色的。
4.如果一个结点是红色的,那么它的两个子结点是黑色的。
5.每个结点到后代的叶结点的简单路径上的黑色结点个数相同。
*/ // 规定颜色
const int RED = 0;
const int BLACK = 1; struct RBTreeNode {
int key;
int color; // 颜色
RBTreeNode * p; // 父节点
RBTreeNode * left; // 左孩子
RBTreeNode * right; // 右孩子
} * NIL, * root; /// 初始化
void init() {
NIL = new RBTreeNode;
NIL->color = BLACK;
NIL->p = NULL;
NIL->left = NULL;
NIL->right = NULL;
root = NIL;
} /// 新建节点
RBTreeNode * create_node(int key) {
RBTreeNode * p = new RBTreeNode;
p->key = key;
p->p = NIL;
p->left = NIL;
p->right = NIL;
p->color = RED;
return p;
} /// 根据键查询
RBTreeNode * search_node(int key) {
RBTreeNode * x = root;
while(x!=NIL && x->key != key) {
if (key < x->key) x = x->left;
else x = x->right;
}
return x;
} /// 查找某子树最小结点
RBTreeNode * search_minimum(RBTreeNode * p) {
if (p == NIL) return NIL;
while(p->left != NIL) p = p->left;
return p;
} /// 查找某子树最大结点
RBTreeNode * search_maximum(RBTreeNode * p) {
if (p == NIL) return NIL;
while (p->right != NIL) p = p->right;
return p;
} /// 查询结点前驱结点(结点)
RBTreeNode * search_predecessor(RBTreeNode * p) {
if (p == NIL) return NIL;
if (p->left != NIL) {
return search_maximum(p->left); // 拥有左子树,后继一定是左子树的最大节点
} else {
RBTreeNode * y = p->p;
while(y!=NIL && y->left==p) { // 找到高层节点中以p所在的树为右子树的树的根节点,即是前驱节点
p = y;
y = y->p;
}
return y;
}
} /// 查找结点后继节点(结点)
RBTreeNode * search_successor(RBTreeNode * p) {
if (p == NIL) return NIL;
if (p->right != NIL) {
return search_minimum(p->right); // 拥有右子树,后继一定是右子树的最小节点
} else {
RBTreeNode * y = p->p;
while(y!=NIL && y->right==p) { // 找到高层节点中以p所在的树为左子树的树的根节点,即是后继节点
p = y;
y = y->p;
}
return y;
}
} /// 替换子树, u被v替换
void transplant_node(RBTreeNode * u, RBTreeNode * v) {
if (u->p == NIL) {
root = v;
} else if (u->p->left == u) {
u->p->left = v;
} else u->p->right = v;
if (v != NIL) {
v->p = u->p;
}
} /// 结点左旋(x, y不为NIL)
void left_rotate(RBTreeNode * x) {
RBTreeNode * y = x->right;
transplant_node(x, y);
RBTreeNode * z = y->left;
x->p = y;
y->left = x;
if (z != NIL) z->p = x;
x->right = z;
} /// 结点右旋(x, y不为NIL)
void right_rotate(RBTreeNode * x) {
RBTreeNode * y = x->left;
transplant_node(x, y);
RBTreeNode * z = y->right;
x->p = y;
y->right = x;
if (z != NIL) z->p = x;
x->left = z;
} /// 插入结点调整
void insert_node_fixup(RBTreeNode * x) {
while (x->p->color == RED) {
RBTreeNode * y = x->p;
if (y->p->left == y) { // 位于爷结点的左子树
RBTreeNode * z = y->p->right;
if (z->color == RED) { // case1: 叔结点是红色
z->color = BLACK;
y->color = BLACK;
y->p->color = RED;
x = y->p;
continue;
}
if (y->right == x) { // case2: 叔结点是黑色,是父结点的右孩子
x = x->p;
left_rotate(y);
}
x->p->color = BLACK; // case3: 叔结点是黑色,是父结点的左孩子
x->p->p->color = RED;
right_rotate(x->p->p);
} else { // 位于爷结点的右子树
RBTreeNode * z = y->p->left;
if (z->color == RED) {
z->color = BLACK;
y->color = BLACK;
y->p->color = RED;
x = y->p;
continue;
}
if (y->left == x) {
x = x->p;
right_rotate(y);
}
x->p->color = BLACK;
x->p->p->color = RED;
left_rotate(x->p->p);
}
}
root->color = BLACK;
} /// 插入结点(结点)
void insert_node(RBTreeNode * z) {
RBTreeNode * x = root;
RBTreeNode * y = NIL;
while (x!=NIL) {
y = x;
if (z->key < x->key) x = x->left;
else x = x->right;
}
z->p = y;
if (y == NIL)
root = z;
else if (z->key < y->key)
y->left = z;
else
y->right = z;
insert_node_fixup(z);
} /// 调整删除结点
void delete_node_fixup(RBTreeNode * x) {
while(x != root && x->color == BLACK) {
if (x->p->left == x) {
RBTreeNode * w = x->p->right;
if (w->color == RED) { // case1: 兄弟结点是红色
x->p->color = RED;
w->color = BLACK;
left_rotate(x->p);
}
if (w->left->color == BLACK && w->right->color == BLACK) { // case2: 兄弟结点是黑色,并且双亲为黑色
w->color = RED;
x = x->p;
continue;
}
if (w->right->color != RED) { // case3: 兄弟结点是黑色,左孩子为红色
w->left->color = BLACK;
w->color = RED;
right_rotate(w);
}
// case4: 兄弟结点是黑色,右孩子是红色
w->color = x->p->color;
w->right->color = BLACK;
x->p->color = BLACK;
left_rotate(x->p);
x = root;
} else {
RBTreeNode * w = x->p->left;
if (w->color == RED) { // case1: 兄弟结点是红色
x->p->color = RED;
w->color = BLACK;
right_rotate(x->p);
}
if (w->right->color == BLACK && w->left->color == BLACK) { // case2: 兄弟结点是黑色,并且双亲为黑色
w->color = RED;
x = x->p;
continue;
}
if (w->left->color != RED) { // case3: 兄弟结点是黑色,左孩子为红色
w->right->color = BLACK;
w->color = RED;
left_rotate(w);
}
// case4: 兄弟结点是黑色,右孩子是红色
w->color = x->p->color;
w->left->color = BLACK;
x->p->color = BLACK;
right_rotate(x->p);
x = root;
}
}
x->color = BLACK;
} /// 删除结点(结点)
void delete_node(RBTreeNode * z) {
RBTreeNode * x; // 记录被删除的结点在树中所处的位置
RBTreeNode * y = z; // 记录实际被删除的结点
int y_origin_color = y->color;
if (z->left == NIL) {
x = z->right;
transplant_node(z, z->right);
} else if (z->right == NIL) {
x = z->left;
transplant_node(z, z->left);
} else { // 左右孩子都存在的情况
y = search_minimum(z->right); // 找后继节点
y_origin_color = y->color;
x = y->right;
if (y != x->right) { // 如果后继不是右孩子,需要变形。将后继节点提为右子树的根节点
transplant_node(y, y->right); // 后继节点的左孩子一定不存在,右孩子取代后继节点
y->right = z->right;
y->right->p = y;
}
// 后继就是右孩子
transplant_node(z, y);
y->left = z->left; // 替换后还需要修改与左子树的父子关系与颜色
z->left->p = y;
y->color = z->color;
}
delete z;
if (y_origin_color == BLACK) delete_node_fixup(x);
} /** --- */ bool insert_node(int key) {
RBTreeNode * node = search_node(key);
if (node != NIL) return false;
node = create_node(key);
insert_node(node);
return true;
} bool delete_node(int key) {
RBTreeNode * node = search_node(key);
if (node == NIL) return false;
delete_node(node);
return true;
} int main() {
init(); RBTreeNode * x = NIL;
if (x == NIL){
printf("==\n");
} else {
printf("!=\n");
} insert_node(1);
// insert_node(3);
// insert_node(5);
// insert_node(7);
// insert_node(9);
//
// insert_node(2);
// insert_node(4);
// insert_node(6);
// insert_node(8);
// insert_node(10);
//
// delete_node(3);
// delete_node(7);
// delete_node(6);
// delete_node(1); while (1) {
int k;
scanf("%d", &k);
RBTreeNode * p = search_node(k);
if (p == NIL) printf("NIL\n");
else printf("OK!\n");
} return 0;
}

红黑树实现(c/c++)的更多相关文章

  1. 红黑树——算法导论(15)

    1. 什么是红黑树 (1) 简介     上一篇我们介绍了基本动态集合操作时间复杂度均为O(h)的二叉搜索树.但遗憾的是,只有当二叉搜索树高度较低时,这些集合操作才会较快:即当树的高度较高(甚至一种极 ...

  2. jdk源码分析红黑树——插入篇

    红黑树是自平衡的排序树,自平衡的优点是减少遍历的节点,所以效率会高.如果是非平衡的二叉树,当顺序或逆序插入的时候,查找动作很可能会遍历n个节点 红黑树的规则很容易理解,但是维护这个规则难. 一.规则 ...

  3. 谈c++ pb_ds库(二) 红黑树大法好

    厉害了,没想到翻翻pb_ds库看到这么多好东西,封装好的.现成的splay.红黑树.avl... 即使不能在考场上使用也可以用来对拍哦 声明/头文件 #include <ext/pb_ds/tr ...

  4. 定时器管理:nginx的红黑树和libevent的堆

    libevent 发生超时后, while循环一次从堆顶del timer——直到最新调整的最小堆顶不是超时事件为止,(实际是del event),但是会稍后把这个timeout的 event放到ac ...

  5. 从2-3-4树到红黑树(下) Java与C的实现

    欢迎探讨,如有错误敬请指正 如需转载,请注明出处   http://www.cnblogs.com/nullzx/ 相关博客: 从2-3-4树到红黑树(上) 从2-3-4树到红黑树(中) 1. 实现技 ...

  6. 红黑树/B+树/AVL树

    RB Tree 红黑树  :http://blog.csdn.net/very_2/article/details/5722682 Nginx的RBTree实现   :http://blog.csdn ...

  7. 论AVL树与红黑树

    首先讲解一下AVL树: 例如,我们要输入这样一串数字,10,9,8,7,15,20这样一串数字来建立AVL树 1,首先输入10,得到一个根结点10 2,然后输入9, 得到10这个根结点一个左孩子结点9 ...

  8. DataStructure——红黑树学习笔记

    1.前言 本文伪码和解释参考: http://blog.csdn.net/v_JULY_v/article/details/6105630 C实现的源码本文未贴出,请见: http://blog.cs ...

  9. 红黑树(Red-Black tree)

    红黑树又称红-黑二叉树,它首先是一颗二叉树,它具体二叉树所有的特性.同时红黑树更是一颗自平衡的排序二叉树.我们知道一颗基本的二叉树他们都需要满足一个基本性质–即树中的任何节点的值大于它的左子节点,且小 ...

  10. map,hash_map, hash_table, 红黑树 的原理和使用

    在刷算法题的时候总是碰到好多题,号称可以用hash table来解题.然后就蒙圈了. 1.首先,map和hash_map的区别和使用: (1)map底层用红黑树实现,hash_map底层用hash_t ...

随机推荐

  1. LG2766 最长不下降子序列问题 最大流 网络流24题

    问题描述 LG2766 题解 \(\mathrm{Subtask 1}\) 一个求最长不下降子序列的问题,发现\(n \le 500\),直接\(O(n^2)\)暴力DP即可. \(\mathrm{S ...

  2. springboot配置spring security 静态资源不能访问

    在springboot整合spring security 过程中曾遇到下面问题:(spring boot 2.0以上版本   spring security 5.x    (spring  secur ...

  3. 每天一道Rust-LeetCode(2019-06-11)

    每天一道Rust-LeetCode(2019-06-02) Z 字形变换 坚持每天一道题,刷题学习Rust. 题目描述 全排列 II 给定一个可包含重复数字的序列,返回所有不重复的全排列. 示例: 输 ...

  4. MySQL实战45讲学习笔记:第十八讲

    一.引子 在 MySQL 中,有很多看上去逻辑相同,但性能却差异巨大的 SQL 语句.对这些语句使用不当的话,就会不经意间导致整个数据库的压力变大. 我今天挑选了三个这样的案例和你分享.希望再遇到相似 ...

  5. [LeetCode] 435. Non-overlapping Intervals 非重叠区间

    Given a collection of intervals, find the minimum number of intervals you need to remove to make the ...

  6. [LeetCode] 30. Substring with Concatenation of All Words 串联所有单词的子串

    You are given a string, s, and a list of words, words, that are all of the same length. Find all sta ...

  7. java web开发入门五(ssh整合)基于intellig idea

    SSH整合 1.引入jar包 Struts 核心jar Hibernate 核心jar Spring Core  核心功能 Web  对web模块支持 Aop   aop支持 Orm   对hiber ...

  8. Django初见

    什么市WEB应用? 所谓的web应用就是我们基于浏览器打开的一个个网页(对应网址得到的内容) 软件开发架构 C/S架构: 客户端/服务端 B/S架构:浏览器/服务器 所有的B/S架构本质上就是C/S架 ...

  9. 【操作系统之十三】Netfilter与iptables

    一.Netfilter Netfilter是由Rusty Russell提出的Linux 2.4内核防火墙框架,该框架既简洁又灵活,可实现安全策略应用中的许多功能,如数据包过滤.数据包处理.地址伪装. ...

  10. NPOI中的PhysicalNumberOfCells 与 LastCellNum

    这两个理论上一个是算空列,一个不算空列,但是实际上有时候相同数据的excel,不同的excel编辑工具保存后的,PhysicalNumberOfCells可能不一样,会导致莫名其妙错误,最好使用Las ...