这一次我改用c++了,因为我嫌弃java没有指针。。。
写在前面,网上有红黑树插入删除可视化模拟器,可以插入删除规定范围内的数,可以感受感受,尤其是删除
-
https://sandbox.runjs.cn/show/2nngvn8w
对于这个网址,亲测无效,不知道你读这篇文章的时候有没有效。
-
链接:https://pan.baidu.com/s/1_c7juIdNxjBPBSqHDPT7uA
提取码:4jcn
这个是我自己上传的,也是从网上找到的,如果有差池,那可能是我上传的出错了。亲测有效。
哈哈,红黑树,听起来好难得样子,其实呢?其实也很难!
废话不多说,入主题。
您需要准备以下知识点:
二叉搜索树
指二叉树满足:
左子树小于根
右子树大于根
左右子树都是二叉搜索树
示例:
更严格的定义,二叉搜索树的中序遍历是升序的。(其实大家都知道这个,没几个人提到?)
二叉平衡树
满足:
左子树和右子树高度差的绝对值不大于1
左右子树都是二叉平衡树
以下这一棵树严重畸形(不平衡):
对于二叉搜索树,我原来就有一点不太明白。比如当b>a时,b是当a的右子节点还是a当b的左子节点?对效率有影响吗?(这里先埋一个伏笔,很深)
有时候有。如果正好形成了一颗畸形树,将会非常非常慢,退化成链,当搜索的时候,一点一点爬,比蜗牛还慢。。。。
如果能保持平衡,那基本就是O(logn)的复杂度。
怎么保持平衡呢?红黑树登场了。
红黑树应满足:
- 每一个节点非黑即红。
- 黑土种的树,树根是黑的。
- 每一个节点的空子位置都挂着虚拟节点NULL,虚拟节点也是黑的。说明一下,下面画的图大部分没有画出虚拟节点,请打开脑洞,想象那里有一个虚拟节点。
- 红节点的每一个孩子必须是黑的(虚拟节点也算孩子呵呵呵)。推论:如果这个节点是红的,它的父亲必定不是红的(否则这个节点就是黑的),说明它的父亲必定为黑色,即父子不能都是红的,不代表黑色不行。
- 对于任意一个节点,它到任意一个后代叶子节点的路线中黑色节点相同。
- 红黑树是二叉搜索树,接近二叉平衡树。
- 有黑子,必有两个子。
好复杂鸭鸭!!怎么那么难??嘿嘿(●ˇ∀ˇ●),还没到最难的地方呢。。。
举个栗子吧
到这里了,大家应该了解红黑树的性质了吧,现在我们就要鼓捣鼓捣各种操作了。。。
哦!!对了,还有“伏笔”呢,这里说明一下吧:
其实在二叉搜索树里,a的右子节点是b,而变成b的左子节点是a的这一步,叫做对a节点左旋
那这下子b原来的左子节点c交给谁管呢?那就交给a管吧,当它的右子节点。
其实可以想象为c先连接a,然后断开和b的连接,提起来b,就完成左旋了。这时候你会发现左旋并不破坏原有的搜索树性质,只会更改平衡性质(可能是变好也可能是变坏)。
上图吧:
右旋就是正好反过来,对于左旋,一切“左”变成“右”,一切“右”变成“左”,(连名字都遵循这规矩),就是右旋了。
以下段落为替换出来的结果:
其实在二叉搜索树里,a的左子节点是b,而变成b的右子节点是a的这一步,叫做对a节点右旋
那这下子b原来的右子节点c交给谁管呢?那就交给a管吧,当它的左子节点。
其实可以想象为c先连接a,然后断开和b的连接,提起来b,就完成右旋了。这时候你会发现右旋并不破坏原有的搜索树性质,只会更改平衡性质(可能是变好也可能是变坏)。
哈哈,其实我也挺懒的,直接替换就行了(顺便提一下,这里替换是有技巧的,三变量交换。左替换成哈喽,右替换成左,哈喽替换成右哈哈哈)
又跑题了。右旋也上个图吧:
还有一种叫变色,就是把颜色变一下(变成你想要的颜色),很容易理解吧。。。
现在鼓捣操作:
查找
查找其实和二叉搜索树一样:
注意!这些函数都是在类Node里面的!
T find(K ky){
if(key==ky)return data;//命中目标
else if(ky<key && left != NULL)return left->find(ky);
else if(ky>key && right != NULL)return right->find(ky);
else return NULL;//失败
}
因为红黑树是平衡的,所以复杂度也就差不多lgn,不会太慢,这背后的一切……
都是插入删除在不停的维护平衡啊!!!
插入
插入首先要找到要插入的位置。
和查找差不多。
void InsFix(Node* n){
}
bool insert(Node* node){
if(key==node->key){//更新
data=node->data;
return true;
}
bool res=false;
if(node->key < key){//实际比当前小,往左走
if(left != NULL)res=left->insert(node);
else{
left=node;node->fa=this;
}
}else{//实际比当前大,往右走
if(right != NULL)res=right->insert(node);
else{
right=node;node->fa=this;
}
}
InsFix(node);
return res;
}
要插入什么颜色呢?当然是红色,因为红色不会太破坏平衡,能不破坏就不破坏。相反地,黑色特别容易破坏平衡,怪不得是黑色呢(联想一下黑客,就知道为什么是黑色了)。
InsFix是什么呢?就是要修补不平衡的情况:
其实这些情况是在插入函数内完成的:
1.树是空树:
设为根节点,并把颜色设为黑色
其实呢,这一种情况没有显式地表现出来。
调用insert的时候,必须有一个节点(有一个节点就不是空树了!!),使用它来调用insert,在子树插入。
此时,其实树不是空树。
那什么时候会出现这一种情况呢?
在这时:
Node* node;
默认就是黑色的(你可以指定它是黑色的,到文末代码部分时就会明白),所以它还是隐式地体现的。
2.节点已存在:
刷新数据
3.父节点为黑色(默认):
插入。
接下来要在InsFix实现:
4.父节点是红节点:
现在已经明显不符合性质4(红之子必黑)了。就需要修补了:
4.1.叔叔有并为红色:
这是其中一种情况,还有的其实都一样,叔叔和爸爸变黑,爷爷变红。
这下得麻烦祖辈搞一搞平衡的事情了。。。
上面有个问题,如果爷爷是根肿么办?
那就把他的颜色硬拉回黑的呗,也不会破坏平衡,为什么?
假设会,说明是对于太爷爷,左子方向多了一个黑,右子没多黑,就不平衡了。
嗯???不是说好的爷爷就是首代了吗,还有爹?这个爷爷已经是女娲捏出来的了,真的不存在所谓的“太爷爷”了!
所以矛盾(在反证的时候最喜欢说矛盾二字了哈哈哈)
哈哈,不用管喽!!!😜○( ^皿^)っHiahiahia…
4.2.叔叔无或为黑:
为了精简,以下4.2.n为:
上面一个标号的节点表示当前插入后的节点,它的标号n代表这个位置会在4.2.n说到
4.2.0
爹变黑,爷爷变红,对爷爷右旋。
其实自己(刚插入的节点)变黑在右旋也行,只不过不想在找祖辈麻烦了~~
如果麻烦了,等着T飞就对了<( ̄︶ ̄)↗[GO!]
4.2.1
其实后面的部分是Ctrl+CV过来的,添加了点东西而已ο(=•ω<=)ρ⌒☆
对b左旋,InsFix(b)就OK了。
4.2.3
啊啊啊啊????怎么0,1,蹦到3了??应该是2吧。难道是打错了??
NO!因为4.2.2是基于4.2.3的,所以先来这个(✿◕‿◕✿)
(⊙_⊙)?
(⊙﹏⊙)
没见过数数这么数的鸭。。。
0,1,3???!!!!!!
Pia!(o ‵-′)ノ”(ノ﹏<。)
爸爸变黑,爷爷变红,对爷爷左旋。
4.2.2
呵呵,这回回到2了。
对b右旋,InsFix(b),OK。
插入情景完~~
.<{=....
灵魂拷问,你真的理解了吗?
来一道题吧。。。加深印象
do{
读一遍所有插入情景;
}while(!会做题);
答案
最后贴上代码:
void leftturn() {
Node* myfa = fa;
Node* rl = right->left;
fa = right;
right->left = this;
right->fa = myfa;
if (myfa != NULL) {
if (myfa->left == this)myfa->left = right;
else myfa->right = right;
}
right = rl;
if (rl != NULL)right->fa = this;
}
void rightturn() {
Node* myfa = fa;
Node* lr = left->right;
fa = left;
left->right = this;
left->fa = myfa;
if (myfa != NULL) {
if (myfa->left == this)myfa->left = left;
else myfa->right = left;
}
left = lr;
if (lr != NULL)left->fa = this;
}
void InsFix(Node* n) {
if (n->fa->color == true) {
Node* l = n->fa->fa->left;
Node* r = n->fa->fa->right;
Node* uncle = (l == n->fa ? r : l);
if (uncle == NULL || uncle->color == false) {//叔叔黑
int c = ((n->fa->fa->right == n->fa) << 1) + (n->fa->right == n);//判断情况
switch (c) {
case 0:
n->fa->color = false;//爸爸变黑
n->fa->fa->color = true;//爷爷变红
n->fa->fa->rightturn();//爷爷右旋
break;
case 1:
n->fa->leftturn();//爸爸左旋
InsFix(n->left);//再做case0处理
break;
case 2:
n->fa->rightturn();//爸爸右旋
InsFix(n->right);//再做case3处理
break;
case 3:
n->fa->color = false;//爸爸变黑
n->fa->fa->color = true;//爷爷变红
n->fa->fa->leftturn();//爷爷左旋
break;
}
}
else if (uncle->color == true) {//叔叔是红的
n->fa->fa->left->color = false;
n->fa->fa->right->color = false;//把爷爷的两个孩子(必有一个爸爸和一个叔叔)都设成黑色的
if (n->fa->fa->fa != NULL)
{
n->fa->fa->color = true;
InsFix(n->fa->fa);
}
}
}
}
bool insert(Node* node) {
if (key == node->key) {//更新
data = node->data;
return true;
}
bool res = false;
if (node->key < key) {//实际比当前小,往左走
if (left != NULL)res = left->insert(node);
else {
node->color = true;
left = node; node->fa = this;
InsFix(node);
}
}
else {//实际比当前大,往右走
if (right != NULL)res = right->insert(node);
else {
node->color = true;
right = node; node->fa = this;
InsFix(node);
}
}
return res;
}
如果你跳出来了那个dowhile循环(见上文),恭喜你,你将会迎来新的循环(在后面),但保证新的循环是最后一个了。。。
插入是不是很复杂?哈哈哈,还有更难的。但别跑哦
,胜利就在前方!!
删除
这将会是全文最难的部分,各位看官要留一留神了,一不小心就会晕倒(难晕了(((φ(◎ロ◎;)φ)))),注意休息。。
网上都这么说:
删除节点就相当于删除替代节点
我看了差不多可以说是一脸懵???一会又后继节点,一会又不后继节点的,真的好晕乎啊。。。(⊙﹏⊙)
梳理一下吧:
-
为什么要替代节点?
想一下就知道,如果把一个上有老下有小的中年油腻男给干掉了,老和小怎么连接吧?? -
谁来替代?
别人说的后继节点替代,我就想知道难道要删孙子爷爷来替代???
所以!更确切地来说,是这样找替代的(有点复杂呦呦呦):
case 1:没有子节点
平衡一下,就扔掉节点就完了。
case 2:一个子节点
用子节点替代
然后让它找替代。
case 3:两个子节点
右节点替代。如果右节点有左子节点,一直向左找,找到尽头。
找到替代节点后把数据拷过来,然后在让它自己去找替代,找不着就是自爆。
这个涉及到递归,真的好复杂。。。╯︿╰
这个是一个很容易。。。。。。。出错的部分,也许本小白也出错了。。。
bool DelFix() {
}
bool remove() {
Node* r = this;
int counter = 0;
int wh = 0;
if (left != NULL)counter++, wh++;
if (right != NULL)counter++, wh += 2;
switch (counter) {
case 0:
if (fa == NULL) { return true; }//NP
DelFix();
if (this == fa->left)fa->left = NULL;
else fa->right = NULL;
//(this == fa->left ? fa->left : fa->right) = NULL;
//为何还认原来的爹是因为很可能需要用这个节点找根节点,如果要插入到其他地方爹会改的。。。
break;
case 1:
r = (wh == 1 ? left : right);
//if (r == NULL)throw "12345";
key = r->key;
data = r->data;
r->remove();
break;
case 2:
r = right;
while (r->left != NULL)
{
r = r->left;
}
key = r->key;
data = r->data;
r->remove();
break;
}
return false;
}
Node *del(K ky,bool &dd) {
dd = false;
if (key == ky) {
dd=remove();
return this;
}//命中目标
else if (ky < key && left != NULL)return left->del(ky,dd);
else if (ky > key&& right != NULL)return right->del(ky,dd);
else return NULL;//失败
}
好了,希望大家能够理解吧(确实有点难o_o …)
下面正式进入各种情景部分。
1.删除红色节点
四个字:运气太好!恭喜你,复杂情况暂时告别你了○( ^皿^)っHiahiahia…直接删除就行liao!
2.删除黑色节点
这才是正常情况,运气一般般。
2.1删除节点是长子
2.1.1 兄弟是红色的
把兄弟变成黑色,爸爸变成红色,对爸爸左旋,得到后面情景。
这里是其中一种,其实不确定到底后面哪一个情况。。。
对于这一种,这里有一个问题。a如果是虚拟的怎么办?其实不标红就行了。为啥?如果原来这里就不存在,你会发现把要删除的节点直接删除了就可以了。这时实际上(指不算虚拟节点),原来删除节点的爸爸已经没有子节点了,此时啥都完事了。从祖辈到那个爸爸就是头了,已经下不去了,所以就完了。
嗯?(⊙o⊙)?这个。。。不平衡鸭鸭鸭!!
你忘啦?不是一会要删除的嘛嘛,怎么就不算帐了额。。
如果删了,你会发现恢复了平衡。
如果不删,那就是不删的问题了。
2.1.2 兄弟是黑的
2.1.2.1 右侄子红色
弟弟颜色变成爸爸的,爸爸变黑,右侄子变黑。对爸爸左旋。
2.1.2.2 右侄子黑色,左侄子红色。
这个只需要把左侄子变黑,弟弟变红,然后对弟弟右旋,得到上一个情景。
2.1.2.3侄子全是黑的
这下怎么办呢?有一点复杂,又分出岔路来了。
首先左边少了一个黑的,右边也该少一个吧,先把弟弟涂红。
2.1.2.3.1 爸爸是红色的
这个其实就是上面所谓的“后面情景”。当然了。。也可能不是这个情景( ̄▽ ̄)"。别看情景号码长,这个甚至可以号称黑色最简情景!根本用不着旋转。其实麻烦的在后面呢,
想一想,当兄弟涂红后,左边消失后,谁来继承黑色大业(否则爸爸这一边少了个黑色)?只能爸爸反继承了。所以要把爸爸标黑。此时就完了。( ̄︶ ̄)↗
2.1.2.3.2 爸爸是黑色的
此时四个字,运气太差!这时要找祖先了。在弟弟标红后,就该找爸爸,再做平衡了。如果找到了根节点,那没有弟弟了,就退出就对了。
你以为删除就这么完了??太天真了!!还没考虑是爸爸的次子呢!
2.2 删除节点是次子
其实说实话,这就是给反过来而已。。。也差不多了,为了省事,就不贴图了。。。(✿◕‿◕✿)
如果不明白,看我不一定靠谱の代码。
2.2.1兄弟节点红色
记得原来怎么弄的吗?现在改一下:哥哥变黑,爸爸变红,爸爸右旋。重新处理。
2.2.2 兄弟节点黑色
2.2.2.1 左侄子红色
哥哥颜色变成爸爸的,爸爸变黑,左侄子变黑。对爸爸右旋。
2.2.2.2 左侄子黑色,右侄子红色
右侄子变黑,哥哥变红,对哥哥左旋,得到上面情景↑。
2.2.2.3 侄子皆黑
哥哥先变红。
2.1.2.3.1 爸爸是红色的
爸爸标黑。
2.1.2.3.2 爸爸是黑色的
找爸爸。
啊!!删除终于地玩完了!!
φ(゜▽゜*)♪
[]~( ̄▽ ̄)~*<
( ̄︶ ̄)↗[GO!]
(●’◡’●)
void DelFix() {
if (fa == NULL)return;//找到根节点,没得玩了。
else if (color)return;//中了一个亿彩票大运
else {
//吃亏常态
if (this == fa->left) {//长子
if (/*(fa->right != NULL) && */fa->right->color == true) {
fa->color = true;
fa->right->color = false;
fa->leftturn();
//fa->color = false;
//if (fa->right != NULL)fa->right->color = true;
DelFix();//重新判断情景。
}
else {
if ((fa->right->left == NULL || fa->right->left->color == false)
&&
(fa->right->right == NULL || fa->right->right->color == false)
)
{
fa->right->color = true;
if (fa->color) {
fa->color = false;
}
else {
fa->DelFix();
}
}
else if ((!(fa->right->right == NULL)) && fa->right->right->color == true) {
fa->right->color = fa->color;
fa->color = false;
fa->right->right->color = false;
fa->leftturn();
}
else if ((fa->right->right == NULL || fa->right->right->color == false) && fa->right->left->color == true) {
fa->right->left->color = false;
fa->right->color = true;
fa->right->rightturn();
fa->right->color = fa->color;
fa->color = false;
//if (fa->right->right == NULL)throw "123456";
fa->right->right->color = false;
fa->leftturn();
}
}
}
else {
if (/*(!(fa->left == NULL)) && */fa->left->color) {
fa->left->color = false;
fa->color = true;
fa->rightturn();
DelFix();//重新判断情景。
//fa->color = false;
//if (fa->left != NULL)fa->left->color = true;
}
else {
if ((fa->left->right == NULL || fa->left->right->color == false)
&&
(fa->left->left == NULL || fa->left->left->color == false)
) {
fa->left->color = true;
if (fa->color == true) {
fa->color = false;
}
else {
fa->DelFix();
}
}
else if((!(fa->left->left == NULL)) && fa->left->left->color)
{
fa->left->color = fa->color;
fa->color = false;
fa->left->left->color = false;
fa->rightturn();
}
else {
fa->left->right->color = false;
fa->left->color = true;
fa->left->leftturn();
fa->left->color = fa->color;
fa->color = false;
//if (fa->left->left == NULL)throw "234";
fa->left->left->color = false;
fa->rightturn();
}
}
}
}
}
bool remove() {
Node* r = this;
int counter = 0;
int wh = 0;
if (left != NULL)counter++, wh++;
if (right != NULL)counter++, wh += 2;
switch (counter) {
case 0:
if (fa == NULL) { return true; }//NP
DelFix();
if (this == fa->left)fa->left = NULL;
else fa->right = NULL;
//(this == fa->left ? fa->left : fa->right) = NULL;
//为何还认原来的爹是因为很可能需要用这个节点找根节点,如果要插入到其他地方爹会改的。。。
break;
case 1:
r = (wh == 1 ? left : right);
//if (r == NULL)throw "12345";
key = r->key;
data = r->data;
r->remove();
break;
case 2:
r = right;
while (r->left != NULL)
{
r = r->left;
}
key = r->key;
data = r->data;
r->remove();
break;
}
return false;
}
Node *del(K ky,bool &dd) {
dd = false;
if (key == ky) {
dd=remove();
return this;
}//命中目标
else if (ky < key && left != NULL)return left->del(ky,dd);
else if (ky > key&& right != NULL)return right->del(ky,dd);
else return NULL;//失败
}
嘿嘿(●ˇ∀ˇ●),我玩完了你还没呢(●’◡’●),题等着你○( ^皿^)っHiahiahia…
对于不理解的各位,请务必要做一做。
do{
读一遍所有删除情景;
}while(!会做题);
答案戳我
趁着热乎,赶紧用红黑树做一道题吧:
问题描述
你有一个盒子,可以扔一个数进去,也可以拿出来一个。
最开始,盒子空空如也,你会做 Q 个操作,操作分为两类:
插入操作:先看一看盒子有没有数 x,如无就把数 x 扔进盒子。
删除操作:看一看盒子有没有数 x,如果有取出 x。
每一个操作,你要输出是否成功执行操作。
输入
第一行一个正整数 Q。
接下来 Q 行描述每个操作。每行 2 个用空格隔开的非负整数 op,x 表述一个操作:op 说明插入还是删除,op=1
则表示这是一个插入操作,op=2 则表示这是一个删除操作输出
按序对每一个操作输出,每个操作输出一行。如成功则输出Succeeded,否则输出Failed。
样例输入
6
1 100
1 100
2 100
1 200
2 100
2 200样例输出
Succeeded
Failed
Succeeded
Succeeded
Failed
Succeeded
这样一道题,我选择哈希表。用红黑树解决冲突。也许麻烦了。。。
// NumBoxRedBlackTree.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#pragma warning(disable : 4996)
#define _OJ_
#include <bits/stdc++.h>
using namespace std;
template<class K, class T>
class Node {
public:
bool color = false;//黑色是false红色是true。默认刚刚创建的是根节点,所以是黑色。
Node* fa = NULL;
Node* left = NULL;
Node* right = NULL;
K key;
T data;
Node() {
}
Node(K ky, T dat) {
key = ky;
data = dat;
}
T find(K ky) {
if (key == ky)return data;//命中目标
else if (ky < key && left != NULL)return left->find(ky);
else if (ky > key&& right != NULL)return right->find(ky);
else return NULL;//失败
}
inline Node* root() {
Node* res = this;
while (res->fa != NULL)res = res->fa;
return res;
}
void leftturn() {
Node* myfa = fa;
Node* rl = right->left;
fa = right;
right->left = this;
right->fa = myfa;
if (myfa != NULL) {
if (myfa->left == this)myfa->left = right;
else myfa->right = right;
}
right = rl;
if (rl != NULL)right->fa = this;
}
void rightturn() {
Node* myfa = fa;
Node* lr = left->right;
fa = left;
left->right = this;
left->fa = myfa;
if (myfa != NULL) {
if (myfa->left == this)myfa->left = left;
else myfa->right = left;
}
left = lr;
if (lr != NULL)left->fa = this;
}
void InsFix(Node* n) {
if (n->fa->color == true) {
//if (n->fa->fa == NULL)throw "1234";
Node* l = n->fa->fa->left;
Node* r = n->fa->fa->right;
Node* uncle = (l == n->fa ? r : l);
if (uncle == NULL || uncle->color == false) {//叔叔黑
int c = ((n->fa->fa->right == n->fa) << 1) + (n->fa->right == n);//判断情况
switch (c) {
case 0:
n->fa->color = false;//爸爸变黑
n->fa->fa->color = true;//爷爷变红
n->fa->fa->rightturn();//爷爷右旋
break;
case 1:
n->fa->leftturn();//爸爸左旋
InsFix(n->left);//再做case0处理
break;
case 2:
n->fa->rightturn();//爸爸右旋
InsFix(n->right);//再做case3处理
break;
case 3:
n->fa->color = false;//爸爸变黑
n->fa->fa->color = true;//爷爷变红
n->fa->fa->leftturn();//爷爷左旋
break;
}
}
else if (uncle->color == true) {//叔叔是红的
n->fa->fa->left->color = false;
n->fa->fa->right->color = false;//把爷爷的两个孩子(必有一个爸爸和一个叔叔)都设成黑色的
if (n->fa->fa->fa != NULL)
{
n->fa->fa->color = true;
InsFix(n->fa->fa);
}
}
}
}
bool insert(Node* node) {
if (key == node->key) {//更新
data = node->data;
return true;
}
bool res = false;
if (node->key < key) {//实际比当前小,往左走
if (left != NULL)res = left->insert(node);
else {
node->color = true;
left = node; node->fa = this;
InsFix(node);
}
}
else {//实际比当前大,往右走
if (right != NULL)res = right->insert(node);
else {
node->color = true;
right = node; node->fa = this;
InsFix(node);
}
}
return res;
}
void DelFix() {
if (fa == NULL)return;//找到根节点,没得玩了。
else if (color)return;//中了一个亿彩票大运
else {
//吃亏常态
if (this == fa->left) {//长子
if (/*(fa->right != NULL) && */fa->right->color == true) {
fa->color = true;
fa->right->color = false;
fa->leftturn();
//fa->color = false;
//if (fa->right != NULL)fa->right->color = true;
DelFix();//重新判断情景。
}
else {
if ((fa->right->left == NULL || fa->right->left->color == false)
&&
(fa->right->right == NULL || fa->right->right->color == false)
)
{
fa->right->color = true;
if (fa->color) {
fa->color = false;
}
else {
fa->DelFix();
}
}
else if ((!(fa->right->right == NULL)) && fa->right->right->color == true) {
fa->right->color = fa->color;
fa->color = false;
fa->right->right->color = false;
fa->leftturn();
}
else if ((fa->right->right == NULL || fa->right->right->color == false) && fa->right->left->color == true) {
fa->right->left->color = false;
fa->right->color = true;
fa->right->rightturn();
fa->right->color = fa->color;
fa->color = false;
//if (fa->right->right == NULL)throw "123456";
fa->right->right->color = false;
fa->leftturn();
}
}
}
else {
if (/*(!(fa->left == NULL)) && */fa->left->color) {
fa->left->color = false;
fa->color = true;
fa->rightturn();
DelFix();//重新判断情景。
//fa->color = false;
//if (fa->left != NULL)fa->left->color = true;
}
else {
if ((fa->left->right == NULL || fa->left->right->color == false)
&&
(fa->left->left == NULL || fa->left->left->color == false)
) {
fa->left->color = true;
if (fa->color == true) {
fa->color = false;
}
else {
fa->DelFix();
}
}
else if((!(fa->left->left == NULL)) && fa->left->left->color)
{
fa->left->color = fa->color;
fa->color = false;
fa->left->left->color = false;
fa->rightturn();
}
else {
fa->left->right->color = false;
fa->left->color = true;
fa->left->leftturn();
fa->left->color = fa->color;
fa->color = false;
//if (fa->left->left == NULL)throw "234";
fa->left->left->color = false;
fa->rightturn();
}
}
}
}
}
bool remove() {
Node* r = this;
int counter = 0;
int wh = 0;
if (left != NULL)counter++, wh++;
if (right != NULL)counter++, wh += 2;
switch (counter) {
case 0:
if (fa == NULL) { return true; }//NP
DelFix();
if (this == fa->left)fa->left = NULL;
else fa->right = NULL;
//(this == fa->left ? fa->left : fa->right) = NULL;
//为何还认原来的爹是因为很可能需要用这个节点找根节点,如果要插入到其他地方爹会改的。。。
break;
case 1:
r = (wh == 1 ? left : right);
//if (r == NULL)throw "12345";
key = r->key;
data = r->data;
r->remove();
break;
case 2:
r = right;
while (r->left != NULL)
{
r = r->left;
}
key = r->key;
data = r->data;
r->remove();
break;
}
return false;
}
Node *del(K ky,bool &dd) {
dd = false;
if (key == ky) {
dd=remove();
return this;
}//命中目标
else if (ky < key && left != NULL)return left->del(ky,dd);
else if (ky > key&& right != NULL)return right->del(ky,dd);
else return NULL;//失败
}
int check() {
int res = 0;
if (left != NULL) {
res += left->check();
if (left->color == false && right == NULL)throw "RBTER";
if (color && left->color)throw "RBTER";
}
if (right != NULL) {
int rr = right->check();
if (rr != res)throw "RBTER";
//res += rr;
if (right->color == false && left == NULL)throw "RBTER";
if (color && right->color)throw "RBTER";
}
if (!color)res++;
return res;
}
};
//void dfs(Node<int, bool>* st) {
// if (st == NULL)return;
// dfs(st->left);
// cout << (st->key) << " ";
// dfs(st->right);
//}
typedef long long num;
typedef Node<num, bool>* numcard;
class rbHash {
private:
const static int p = 202061;//6.1儿童节,我能收到礼物:),所以挑一个61结尾的哈哈哈
numcard ns[p];
inline int hash(num x) {
int res = abs(x % p);
return res;
}
public:
rbHash() {
memset(ns, NULL, sizeof(ns));
}
void k(numcard n) {
if (n->left != NULL)k(n->left);
if (n->right != NULL)k(n->right);
delete n;
}
~rbHash()
{
for (int i = 0; i < p; i++) {
if (ns[i] != NULL)
{
k(ns[i]);
}
}
}
bool insert(num a) {
int loc = hash(a);
numcard n = new Node<num, bool>(a, true);
//if (n == NULL)throw "1324";
if (ns[loc] == NULL) {
ns[loc] = n;
return true;
}
else {
bool r = !(ns[loc]->root()->insert(n));
ns[loc]->root()->check();
return r;
}
}
bool find(num a) {
int loc = hash(a);
if (ns[loc] == NULL)return false;
else return ns[loc]->root()->find(a);
}
bool remove(num a) {
int loc = hash(a);
bool res = true;
if (ns[loc] == NULL)return false;
else {
numcard wd;
bool dd;
//if (ns[loc]->root() == NULL)cout << "uytefuqtewrfuyqtweifryqtweyf";
//int xxx = (ns[loc]->root()->color);
numcard x =ns[loc]->root()->del(a,dd);
if (dd) {
delete ns[loc];
ns[loc] = NULL;
return true;
}
if (x == NULL)res = false;
//if(wd != ns[loc])delete wd;
if (ns[0] != NULL)ns[0]->root()->check();
return res;
}
}
};
void dump(int s) {
cout << "ohohohhhohohohho";
}
int main() {
//throw "123456";
//int datas[] = { 10,40,30,60,90,70,20,50,80,20 };
//int ddts[] = { 90, 60, 50, 0};
//Node<int, bool>* nod = new Node<int, bool>(datas[0], true);
vector<Node<int, bool> > mem;
//for (int i : datas) {
// /*mem.push_back(Node<int, bool>(i, true));*/
// Node<int, bool>* n = new Node<int, bool>(i, true);
// cout<<i<<" "<<(nod->root()->insert(n))<<" ";
//}
//cout << endl;
//for (int i : ddts) {
// cout << i<<" "<<(nod->root()->del(i))<<" ";
//}
//cout << endl;
//dfs(nod->root());
//signal(SIGSEGV, &dump);
#ifndef _OJ_
FILE* stream;
freopen_s(&stream,"C:\\Users\\wangz\\source\\repos\\CDCppDebug\\Debug\\input.txt", "r", stdin);
freopen_s(&stream,"C:\\Users\\wangz\\source\\repos\\CDCppDebug\\Debug\\output.txt", "w", stdout);
#endif // !_OJ_
//std::ios::sync_with_stdio(false);
rbHash *h = new rbHash();
int q;
cin >> q;
for (int i = 0; i < q; i++) {
int x; num y;
cin >> x >> y;
///
///
try {
if (x == 1) {
cout << (h->insert(y) ? "Succeeded\n" : "Failed\n");
}
else {
cout << (h->remove(y) ? "Succeeded\n" : "Failed\n");
}
}
catch (...) {
cout << i;
return 0;
}
}
//delete h;
#ifndef _OJ_
fclose(stdin);
fclose(stdout);
#endif // !_OJ_
return 0;
}
// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单
// 入门使用技巧:
// 1. 使用解决方案资源管理器窗口添加/管理文件
// 2. 使用团队资源管理器窗口连接到源代码管理
// 3. 使用输出窗口查看生成输出和其他消息
// 4. 使用错误列表窗口查看错误
// 5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
// 6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件
读了这么多,你应该很好奇吧(我也是),红黑树的发明者的脑洞怎么这么大??
我也是小白,如果有误,请指正!
.<{=....
THE END