标签:
数据结构之红黑树
红黑树(Red Black Tree) 是一种自平衡二叉查找树
红黑树和AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,
从而获得较高的查找性能。
它虽然是复杂的,但它的最坏情况运行时间也是非常良好的,并且在实践中是高效的:
它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目。(度娘)
C++ stl里面的set,map底层就是用红黑树实现的。
红黑树具体的插入删除原理请参考<<算法导论>> 维基上面也讲得不错。
反正插入过程就是要解决”红-红”冲突,删除就是要解决”黑-黑”冲突。
下面是按照理我解的方式来实现红黑树,我只写了插入和删除过程,
其它的操作和普通的二叉树没多大区别。。
分享一个可以演示红黑树各种操作的网页:
https://www.cs.usfca.edu/~galles/visualization/RedBlack.html
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<ctime>
const int RED= 1;
const int BLACK = 0;
struct Node {
int data;
bool color;
Node *fa, *left, *right;
Node() :color(BLACK), data(0){ fa = left = right = NULL; }
Node(int _v, Node *p) :data(_v), color(RED){ fa = left = right = p; }
};
struct RedBlackTree{
Node *root, *null;
void init(){
null = new Node();
root = null;
}
/*
*
*
*
* 对红黑树的节点(x)进行右旋转
*
* px px
* / /
* -> x y
* / \ / * -> y rx --(右旋)--> ly x
* / \ / * ly ry ry rx
*
*
*
*/
// 旋转操作可以不传递引用(reference)话说传引用会快一些。。。
void rotate_right(Node* &x) {
Node *y = x->left;
x->left = y->right;
if (y->right != null) y->right->fa = x;
y->fa = x->fa;
if (x->fa == null) root = y;
else if (x->fa->left == x) x->fa->left = y;
else x->fa->right = y;
y->right = x;
x->fa = y;
}
// 左旋同上
void rotate_left(Node* &x) {
Node *y = x->right;
x->right = y->left;
if (y->left != null) y->left->fa = x;
y->fa = x->fa;
if (x->fa == null) root = y;
else if (x->fa->left == x) x->fa->left = y;
else x->fa->right = y;
y->left = x;
x->fa = y;
}
Node *find(Node *x, int &data) {
while (x != null && x->data != data) {
x = data < x->data ? x->left : x->right;
}
return x;
}
void insert(int v) {
Node *x = root;
Node *y = null;
while (x != null) { // 先找到带插入节点的位置,y保存该路径上最后一个节点
y = x;
x = v < x->data ? x->left : x->right;
}
x = new Node(v, null);
if (y != null) { // 确定新节点x与其父节点y的大小关系,再将它们连起来
if (v < y->data) y->left = x;
else y->right = x;
} else {
root = x;
}
x->fa = y;
insert_fix(x);
}
void insert_fix(Node* &x) {
while (x->fa->color != BLACK) {
// x为当前节点
// par x的父节点,Gp x的祖父节点, uncle x的叔叔节点
Node *par = x->fa, *Gp = par->fa, *uncle = null;
if (par == Gp->left) { // 若父节点是祖父的左孩子
uncle = Gp->right; // 叔叔为祖父的右孩子
if (uncle->color == RED) { // 若叔叔颜色为红色
// Case1:父亲和叔叔节点变为红色,祖父颜色变为红色
// 将祖父节点设置为当前节点,再继续(因为Gp由黑变成红色,Gp的父节点可能为红色)
// 所以要继续向上递归调整
par->color = BLACK;
Gp->color = RED;
uncle->color = BLACK;
x = Gp;
} else if (x == par->right) {
// Case2:x为右孩子,将父节点设置为当前节点,再进行左旋
// 即变成Case3:
rotate_left(x = par);
} else {
// Case3:x,父节点,祖父节点三者共线(左侧)
// 将祖父节点改为红色,父节点改为黑色
// 此时调整完成,红黑树性质得到恢复
Gp->color = RED;
par->color = BLACK;
rotate_right(Gp);
}
} else {
// 同上,只是由左变成右而已
uncle = Gp->left;
if (uncle->color == RED) {
par->color = BLACK;
Gp->color = RED;
uncle->color = BLACK;
x = Gp;
} else if (x == par->left) {
rotate_right(x = par);
} else {
Gp->color = RED;
par->color = BLACK;
rotate_left(Gp);
}
}
}
// 将根置为黑色
root->color = BLACK;
}
void del(int data){
// 先找到待删除的节点
Node *z = find(root, data);
// 若没找到直接退出
if (z == null) return;
Node *y = z, *x = null;
// 若z的左右孩子均不为空,则用y保存z的右子树中最小的节点
if (z->left != null && z->right != null) {
y= z->right;
while (y->left != null) y = y->left;
}
// 此时y只有左子树,或只有右子树,或左右子树均不存在
// x可能为 null !!!
x = y->left != null ? y->left : y->right;
// 将x与y的父节点连接起来,这里可能会改变null节点的fa指针,但没有关系的。。。
x->fa = y->fa;
if (y->fa == null) root = x;
// 确定y节点与其父亲的左右关系
else if (y->fa->left == y) y->fa->left = x;
else y->fa->right = x;
// 若z不等于y 拷贝数据,y才是真正要删除的节点
if (z != y) z->data = y->data;
// 如果删除的节点y的颜色为黑色,对树进行调整使得树满足红黑树的要求
if (y->color == BLACK) del_fix(x);
// 释放y
#ifdef DE_BUG
delete y;
#endif
}
void del_fix(Node* &x) {
while (x != root && x->color == BLACK) {
// x为当前节点
// par x的父节点, sibling x的兄弟节点
Node *par = x->fa, *sibling = null;
// x为父节点的左孩子
if (par->left == x){
// 兄弟节点则为父节点的右孩子
sibling = par->right;
// 若兄弟节点的颜色为红色
if (sibling->color == RED) {
// Case1:将兄弟节点染成黑色,父节点染成红色
sibling->color = BLACK;
par->color = RED;
// 父节点左旋
rotate_left(par);
// 重新设置兄弟节点
sibling = par->right;
} else
// Case2:兄弟节点为黑色,兄弟节点的左右孩子均为黑色
if (sibling->left->color == BLACK && sibling->right->color == BLACK) {
// 将兄弟节点染成红色
sibling->color = RED;
// 将父节点变成当前节点
x = par;
} else {
// Case3:兄弟节点为黑色,兄弟节点的右孩子为黑色,左孩子为红色
if (sibling->right->color == BLACK) {
// 将兄弟节点的左孩子染成黑色
sibling->left->color = BLACK;
// 将兄弟节点染成红色
sibling->color = RED;
// 将兄弟节点右旋
rotate_right(sibling);
// 重新设置兄弟节点
sibling = par->right;
}
// Case4:兄弟节点为黑色,兄弟节点的右孩子为红色,左孩子为任意颜色
// 将兄弟节点染成父节点的颜色
sibling->color = par->color;
// 父节点染成黑色
par->color = BLACK;
// 兄弟节点的右孩子染成黑色
sibling->right->color = BLACK;
// 将父节点左旋,跳出循环。
rotate_left(par);
break;
}
} else {
// 同上,只是由左变成右而已
sibling = par->left;
if (sibling->color == RED) {
sibling->color = BLACK;
par->color = RED;
rotate_right(par);
sibling = par->left;
} else
if (sibling->left->color == BLACK && sibling->right->color == BLACK) {
sibling->color = RED;
x = par;
} else {
if (sibling->left->color == BLACK){
sibling->right->color = BLACK;
sibling->color = RED;
rotate_left(sibling);
sibling = par->left;
} else {
sibling->color = par->color;
par->color = BLACK;
sibling->left->color = BLACK;
rotate_right(par);
break;
}
}
}
}
x->color = BLACK;
}
}rbt_tree;
int main() {
// 以下为测试
int a = clock();
rbt_tree.init();
for (int i = 0; i < 2000000; i++) {
rbt_tree.insert(i);
}
#ifdef DE_BUG
for (int i = 0; i < 2000000; i++) {
rbt_tree.del(i);
}
#endif
rbt_tree.del(4);
printf("%d\n", clock() - a);
return 0;
}
下面是以bzoj3224/Tyvj 1728普通平衡树 为例实现了红黑树的各种操作
增加了size域(子树的大小),cnt域(关键字出现的次数)。
不想吐槽了,wa的快吐了%>_<% 由于插入,删除均用非递归方式实现的,
导致维护size,cnt域太麻烦了。。。
自己对拍了数据有输出1w多行才出错的(这咋调试啊 (>﹏<) )。还好最后发现了bug。
程序只通过了oj的数据,不知道还有没有隐含的bug →_→ 写的很搓懒得注释了。。。
欢迎各位游客批评指正O(∩_∩)O~~
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<ctime>
const int Max_N = 110000;
struct Node {
int data, s, c;
bool color;
Node *fa, *ch[2];
inline void set(int _v, bool _color, int i, Node *p) {
data = _v, color = _color, s = c = i;
fa = ch[0] = ch[1] = p;
}
inline void push_up() {
s = ch[0]->s + ch[1]->s + c;
}
inline void push_down() {
for (Node *x = this; x->s; x = x->fa) x->s--;
}
inline int cmp(int v) const {
return data == v ? -1 : v > data;
}
};
struct RedBlackTree {
int top;
Node *root, *null;
Node stack[Max_N], *tail, *store[Max_N];
void init() {
tail = &stack[0];
null = tail++;
null->set(0, 0, 0, NULL);
root = null;
top = 0;
}
inline Node *newNode(int v) {
Node *p = null;
if (!top) p = tail++;
else p = store[--top];
p->set(v, 1, 1, null);
return p;
}
inline void rotate(Node* &x, bool d ) {
Node *y = x->ch[!d];
x->ch[!d] = y->ch[d];
if (y->ch[d]->s) y->ch[d]->fa = x;
y->fa = x->fa;
if (!x->fa->s) root = y;
else x->fa->ch[x->fa->ch[0] != x] = y;
y->ch[d] = x;
x->fa = y;
y->s = x->s;
x->push_up();
}
inline void insert(int v) {
Node *x = root, *y = null;
while (x->s) {
x->s++, y = x;
int d = x->cmp(v);
if (-1 == d) {
x->c++;
return;
}
x = x->ch[d];
}
x = newNode(v);
if (y->s) y->ch[v > y->data] = x;
else root = x;
x->fa = y;
insert_fix(x);
}
inline void insert_fix(Node* &x) {
while (x->fa->color) {
Node *par = x->fa, *Gp = par->fa;
bool d = par == Gp->ch[0];
Node *uncle = Gp->ch[d];
if (uncle->color) {
par->color = uncle->color = 0;
Gp->color = 1;
x = Gp;
} else if (x == par->ch[d]) {
rotate(x = par, !d);
} else {
Gp->color = 1;
par->color = 0;
rotate(Gp, d);
}
}
root->color = 0;
}
inline Node *find(Node *x, int data) {
while (x->s && x->data != data) x = x->ch[x->data < data];
return x;
}
inline void del_fix(Node* &x) {
while (x != root && !x->color) {
bool d = x == x->fa->ch[0];
Node *par = x->fa, *sibling = par->ch[d];
if (sibling->color) {
sibling->color = 0;
par->color = 1;
rotate(x->fa, !d);
sibling = par->ch[d];
} else if (!sibling->ch[0]->color && !sibling->ch[1]->color) {
sibling->color = 1, x = par;
} else {
if (!sibling->ch[d]->color) {
sibling->ch[!d]->color = 0;
sibling->color = 1;
rotate(sibling, d);
sibling = par->ch[d];
}
sibling->color = par->color;
sibling->ch[d]->color = par->color = 0;
rotate(par, !d);
break;
}
}
x->color = 0;
}
inline void del(int data) {
Node *z = find(root, data);
if (!z->s) return;
if (z->c > 1) {
z->c--;
z->push_down();
return;
}
Node *y = z, *x = null;
if (z->ch[0]->s && z->ch[1]->s) {
y = z->ch[1];
while (y->ch[0]->s) y = y->ch[0];
}
x = y->ch[!y->ch[0]->s];
x->fa = y->fa;
if (!y->fa->s) root = x;
else y->fa->ch[y->fa->ch[1] == y] = x;
if (z != y) z->data = y->data, z->c = y->c;
y->fa->push_down();
for (Node *k = y->fa; y->c > 1 && k->s && k != z; k = k->fa) k->s -= y->c - 1;
if (!y->color) del_fix(x);
store[top++] = y;
}
inline void kth(int k) {
int t;
Node *x = root;
for (; x->s;) {
t = x->ch[0]->s;
if (k <= t) x = x->ch[0];
else if (t + 1 <= k && k <= t + x->c) break;
else k -= t + x->c, x = x->ch[1];
}
printf("%d\n", x->data);
}
inline void rank(int v) {
int t, cur = 0;
Node *x = root;
for (; x->s;) {
t = x->ch[0]->s;
if (v == x->data) break;
else if (v < x->data) x = x->ch[0];
else cur += t + x->c, x = x->ch[1];
}
printf("%d\n", cur + t + 1);
}
inline void succ(int v) {
int ret = 0;
Node *x = root;
while (x->s) {
if (x->data > v) ret = x->data, x = x->ch[0];
else x = x->ch[1];
}
printf("%d\n", ret);
}
inline void pred(int v) {
int ret = 0;
Node *x = root;
while (x->s) {
if (x->data < v) ret = x->data, x = x->ch[1];
else x = x->ch[0];
}
printf("%d\n", ret);
}
}rbt;
int main() {
#ifdef LOCAL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w+", stdout);
#endif
rbt.init();
int n, op, v;
scanf("%d", &n);
while (n--) {
scanf("%d %d", &op, &v);
if (1 == op) rbt.insert(v);
else if (2 == op) rbt.del(v);
else if (3 == op) rbt.rank(v);
else if (4 == op) rbt.kth(v);
else if (5 == op) rbt.pred(v);
else rbt.succ(v);
}
return 0;
}
标签:
原文地址:http://blog.csdn.net/u012077152/article/details/45749933