声明
需要结合wiki中的讲解看,此源码是对于wiki中源码的修正以及加注释,从而方便理解
wiki链接: 红黑树wiki
代码以及注释
/*
具体见:
https://zh.wikipedia.org/wiki/%E7%BA%A2%E9%BB%91%E6%A0%91
感觉源码的插入有问题,自己改了一下
然后删除中情形二的代码也有问题,wiki源码自己注释反了___版本匹配错了
// ```cpp
if(p == p->parent->leftTree)
//rotate_left(p->sibling());
rotate_left(p->parent);
// ```
应该要这样
// ```cpp
if(p == p->parent->leftTree)
rotate_left(p->sibling());
//rotate_left(p->parent);
// ```
************************
这个版本的左右旋源码核心是使用的那个节点的值是3个点值中的中间大小
************************
然后就是在删除那里卡死了,去看 STL源码剖析 ,发现那本书直接没有讲删除
然后找到一篇知乎文章,讲得比较妙的 B ... 让我瞬间懂了为啥要delete_case
下面我们开始讨论修复操作(下面的叶子节点都是指非NULL的叶子节点):
A. 删除的是叶子节点且该叶子节点是红色的 ---> 无需修复,因为它不会破坏红黑树的5个特性
B. 删除的是叶子节点且该叶子节点是黑色的 ---> 很明显会破坏特性5,需要修复。 ___ 对于其他路的影响
C. 删除的节点(为了便于叙述我们将其称为P)下面有一个子节点 S,对于这种情况我们通过 将P和S的值交换的方式,
巧妙的将删除P变为删除S,S是叶子节点,这样C这种情况就会转 换为A, B这两种情况:
C1: P为黑色,S为红色 ---> 对应 A 这种情况
C2: P为黑色或红色,S为黑色 --- > 对应 B 这种情况
D. 删除的节点有两个子节点,对于这种情况,我们通过将P和它的后继节点N的值交换的方式,
将删除节点P转换为删除后继节点N,而后继节点只可能是以下两种情况:
D1: N是叶子节点 --- > 对应情况 A 或 B
D2: N有一个子节点 ---- > 对应情况 C
所以通过上面的分析我们发现,红黑树节点删除后的修复操作都可以转换为 A 或 B这两种情况,而A不需要修复,
所以我们只需要研究B这种情况如何修复就行了。
*/
#define BLACK 1
#define RED 0
#include <iostream>
using namespace std;
class bst {
private:
struct Node {
int value;
bool color;
Node *leftTree, *rightTree, *parent;
Node() : value(0), color(RED), leftTree(NULL), rightTree(NULL), parent(NULL) { }
Node* grandparent() {
if(parent == NULL){
return NULL;
}
return parent->parent;
}
Node* uncle() {
if(grandparent() == NULL) {
return NULL;
}
if(parent == grandparent()->rightTree)
return grandparent()->leftTree;
else
return grandparent()->rightTree;
}
Node* sibling() {
if(parent->leftTree == this)
return parent->rightTree;
else
return parent->leftTree;
}
};
/*
左右旋核心,掌握5个点的父子关系,分别是y,p,fa,root,gp
先是处理y和fa的父子关系
然后再是p和fa的父子关系
再是p和root的关系
最后是gp和p的父子关系
*/
// 这里的p其实是"右旋"解析图中的 P
// 这个版本的右旋是p作为左儿子的右旋
// 三点中的中值
/*
G(maybe NULL)
/
P
/
N
旋转后:
P
/ \
N G
*/
void rotate_right(Node *p){
Node *gp = p->grandparent();
Node *fa = p->parent;
Node *y = p->rightTree;
fa->leftTree = y;
if(y != NIL)
y->parent = fa;
p->rightTree = fa;
fa->parent = p;
if(root == fa)
root = p;
p->parent = gp;
if(gp != NULL){
if(gp->leftTree == fa)
gp->leftTree = p;
else
gp->rightTree = p;
}
}
// 这里的p其实是"左旋"解析图中的 N
// 这个版本的左旋是p作为父节点的右儿子的左旋
// 三点中的中值
/*
G(maybe NULL)
/
P
\
N
转后:
G
/
N
/
P
*/
void rotate_left(Node *p){
if(p->parent == NULL){
root = p;
return;
}
// gp可能为nullptr的
// 一开始就把很多节点都存起来了,所以超级安全
Node *gp = p->grandparent();
Node *fa = p->parent;
Node *y = p->leftTree;
fa->rightTree = y;
if(y != NIL)
y->parent = fa;
p->leftTree = fa;
fa->parent = p;
if(root == fa)
root = p;
p->parent = gp;
if(gp != NULL){
if(gp->leftTree == fa)
gp->leftTree = p;
else
gp->rightTree = p;
}
}
void inorder(Node *p){
if(p == NIL)
return;
if(p->leftTree)
inorder(p->leftTree);
cout << p->value << " ";
if(p->rightTree)
inorder(p->rightTree);
}
string outputColor (bool color) {
return color ? "BLACK" : "RED";
}
Node* getSmallestChild(Node *p){
if(p->leftTree == NIL)
return p;
return getSmallestChild(p->leftTree);
}
bool delete_child(Node *p, int data){
if(p->value > data){
if(p->leftTree == NIL){
return false;
}
return delete_child(p->leftTree, data);
} else if(p->value < data){
if(p->rightTree == NIL){
return false;
}
return delete_child(p->rightTree, data);
} else if(p->value == data){
if(p->rightTree == NIL){
// 右孩子为NIL,就在左子树上做文章
delete_one_child (p);
return true;
}
// 右孩子不为NIL,就获取右子树中最小的那个节点
// 那个节点要么左孩子为NIL,要么两边都为NIL
// 综合前面的情况可以知道,两个孩子都可能为NIL or 其中的一个
Node *smallest = getSmallestChild(p->rightTree);
swap(p->value, smallest->value);
delete_one_child (smallest);
return true;
}else{
return false;
}
}
void delete_one_child(Node *p){
Node *child = p->leftTree == NIL ? p->rightTree : p->leftTree;
if(p->parent == NULL && p->leftTree == NIL && p->rightTree == NIL){
p = NULL;
root = p;
return;
}
if(p->parent == NULL){
delete p;
child->parent = NULL;
root = child;
root->color = BLACK;
return;
}
if(p->parent->leftTree == p){
p->parent->leftTree = child;
} else {
p->parent->rightTree = child;
}
child->parent = p->parent;
if(p->color == BLACK){
if(child->color == RED){
child->color = BLACK;
} else
// 儿子和被删除的父亲节点都是黑色
// 两个孩子都必须为叶子
// 那还要这个delete_case干嘛??
// 卡了几个小时
// 傻逼了,所以就是这一条路的黑色节点-1!!!会对其他路影响
// 原来要删除的p被删除了,用它两个NIL叶子中的一个顶替了它的位置!
// 所以之后讨论它的兄弟,其实是原来要删除的p的兄弟
// NIL顶替位置
delete_case (child);
}
delete p;
}
void delete_case(Node *p){
if(p->parent == NULL){
p->color = BLACK;
return;
}
// 情形二
// 原来的位置的兄弟为RED,则兄弟存在,不为NIL
if(p->sibling()->color == RED) {
p->parent->color = RED;
p->sibling()->color = BLACK;
if(p == p->parent->leftTree)
rotate_left(p->sibling());
// rotate_left(p->parent);
else
//rotate_right(p->sibling());
rotate_right(p->parent);
}
// 情形3
if(p->parent->color == BLACK && p->sibling()->color == BLACK
&& p->sibling()->leftTree->color == BLACK && p->sibling()->rightTree->color == BLACK) {
p->sibling()->color = RED;
delete_case(p->parent);
} else if(p->parent->color == RED && p->sibling()->color == BLACK
&& p->sibling()->leftTree->color == BLACK && p->sibling()->rightTree->color == BLACK) {
// 情形4
p->sibling()->color = RED;
p->parent->color = BLACK;
} else {
if(p->sibling()->color == BLACK) {
// 情形5__算是一个中间处理
if(p == p->parent->leftTree && p->sibling()->leftTree->color == RED
&& p->sibling()->rightTree->color == BLACK) {
p->sibling()->color = RED;
p->sibling()->leftTree->color = BLACK;
rotate_right(p->sibling()->leftTree);
} else if(p == p->parent->rightTree && p->sibling()->leftTree->color == BLACK
&& p->sibling()->rightTree->color == RED) {
p->sibling()->color = RED;
p->sibling()->rightTree->color = BLACK;
rotate_left(p->sibling()->rightTree);
}
}
// 情形六
// 下面两行的颜色操作根据wiki来说是 没有意义的,
// 本来就是相同的黑色__因为兄弟颜色是红的在 情形二 就处理过了
// 而且父亲是红也在情形4和5处理过了__不对__情形4 和5 没处理完
// 见图片( 文末 ) ___ wiki中没讲的情形7 _代码兼顾了
p->sibling()->color = p->parent->color;
p->parent->color = BLACK;
if(p == p->parent->leftTree){
p->sibling()->rightTree->color = BLACK;
rotate_left(p->sibling());
} else {
p->sibling()->leftTree->color = BLACK;
rotate_right(p->sibling());
}
}
}
void insert(Node *p, int data){
if(p->value >= data){
if(p->leftTree != NIL)
insert(p->leftTree, data);
else {
Node *tmp = new Node();
tmp->value = data;
tmp->leftTree = tmp->rightTree = NIL;
tmp->parent = p;
p->leftTree = tmp;
insert_case (tmp);
}
} else {
if(p->rightTree != NIL)
insert(p->rightTree, data);
else {
Node *tmp = new Node();
tmp->value = data;
tmp->leftTree = tmp->rightTree = NIL;
tmp->parent = p;
p->rightTree = tmp;
insert_case (tmp);
}
}
}
void insert_case(Node *p){
/*
情形1:新节点N位于树的根上,没有父节点。
在这种情形下,我们把它重绘为黑色以满足性质2。
因为它在每个路径上对黑节点数目增加一,性质5符合。
*/
if(p->parent == NULL){
root = p;
p->color = BLACK;
return;
}
/*
情形2(其实是下面if的else,不用写):新节点的父节点P是黑色,所以性质4没有失效
(新节点是红色的)。在这种情形下,树仍是有效的。
性质5也未受到威胁,尽管新节点N有两个黑色叶子子节点;
但由于新节点N是红色,通过它的每个子节点的路径就都有同
通过它所取代的黑色的叶子的路径同样数目的黑色节点,
所以依然满足这个性质。
*/
if(p->parent->color == RED){
// 情形3
if(p->uncle()->color == RED) {
p->parent->color = p->uncle()->color = BLACK;
p->grandparent()->color = RED;
// 相当于在grandparent插入了一个红色节点
insert_case(p->grandparent());
} else {
if(p->parent->rightTree == p && p->grandparent()->leftTree == p->parent) {
rotate_left(p);
p = p->leftTree;
goto left_left;
// 2020年6月3日17:43:52 感觉下面两句都有问题,改改
// p->color = BLACK;
// // 下面这一句有问题吧,万一其中一个是NIL...呢?? 怎么能标红?
// p->leftTree->color = p->rightTree->color = RED;
} else if(p->parent->leftTree == p && p->grandparent()->rightTree == p->parent) {
rotate_right(p);
p = p->rightTree;
goto right_right;
// p->color = BLACK;
// p->leftTree->color = p->rightTree->color = RED;
} else if(p->parent->leftTree == p && p->grandparent()->leftTree == p->parent) {
left_left:
p->parent->color = BLACK;
p->grandparent()->color = RED;
rotate_right(p->parent);
} else if(p->parent->rightTree == p && p->grandparent()->rightTree == p->parent) {
right_right:
p->parent->color = BLACK;
p->grandparent()->color = RED;
rotate_left(p->parent);
}
}
}
}
void DeleteTree(Node *p){
if(!p || p == NIL){
return;
}
DeleteTree(p->leftTree);
DeleteTree(p->rightTree);
delete p;
}
public:
bst() {
NIL = new Node();
NIL->color = BLACK;
root = NULL;
}
~bst() {
if (root)
DeleteTree (root);
delete NIL;
}
void inorder() {
if(root == NULL)
return;
inorder (root);
cout << endl;
}
void insert (int x) {
if(root == NULL){
root = new Node();
root->color = BLACK;
root->leftTree = root->rightTree = NIL;
root->value = x;
} else {
insert(root, x);
}
}
bool delete_value (int data) {
return delete_child(root, data);
}
private:
Node *root, *NIL;
};
int main(int argc, char const *argv[])
{
bst rb_tree;
return 0;
}