开学小半年,Andy家里闲
(省略掉令人头痛的开场部分)
(明明就是偷懒直到最近才想起来更新点博客)
-------------------------------(分割线)---------------------------------
今天我们主讲一波红黑树的实现,作为一个初学小白的总结,也方便和我一样的小白们能获得一篇简单的实(速)现(通)方(教)法(学)。
看过网上很多很多篇关于红黑树的帖子(啊关了浏览器一下子找不到链接...),关于原理以及最最最重要理解的调色和旋转都有讲的非常好的文章
所以提前声明:如果你是想知道“为什么红黑树要这样转/红黑树调色的动画/旋转的演示”这里一定不会讲
这里有的:# 四种插入情况 # 三种删除操作+五(七)种调色方法 #基本的二叉树讲解
首先,复习一下简单的BST的搜索和插入:
1.Left<Parent<Right
搜索一个点:while(now!=Res){
if(now<Res)找左孩子
eles if(now>Res)找右孩子
else if(now==Res) 找到啦
}
插入一个点:搜索+返回末节点指针+记录是左/是右孩子
啊这时候就有小伙伴要问啦,为什么不讲删除呢?
博主不会(doge)(bushi XD)
因为红黑树的删除无法在BST删除的基础上直接增加函数,有很大的改动,所以就不细说咯
那么,基于二叉树的结构,再其上进行适当的改造,就可以成为一棵红黑树。
附上BST代码,有兴趣的小伙伴可以以此为基础尝试将其改造为红黑树
#include<bits/stdc++.h>
using namespace std;
template<class Key,class Value>
class node { // 树的节点
public:
Key key;
Value data;
node* left;
node* right;
node* parent;
node<Key,Value>() {
left = nullptr;
right = nullptr;
parent = nullptr;
}
~node() {
left = nullptr;
right = nullptr;
parent = nullptr;
}
};
template<class Key,class Value> //泛化编程 以后可以做成自己的头文件(啊 当然二叉搜索树就不做了...本身不优秀)
class BItree { // BI(二分)tree树 (非完全二叉树)
public:
typedef node<Key,Value> BItree_node;
typedef BItree_node* node_ptr; //typedef换名称 好处多多 还有逼格(bushi)//当然是为了更清楚的表达代码
protected:
node_ptr header; //树根
int size_counter; //节点数量
public:
BItree() {
header = new BItree_node();
size_counter = 0; //这里用 0 是因为 用户没有给出数据,不认为new出来的算一个节点
}
BItree(Key key,Value data) {
header = new BItree_node();
header->key = key;
header->data = data;
size_counter = 1;
} //构造函数x2
node_ptr& Search(Key sth) { //注意这里要返回“引用”
node_ptr tmp = new BItree_node();
tmp = header; //从树根开始//当然我们有另一种写法:传入一个node_ptr指针:search 成为一个通用函数
if (tmp == NULL) {
return tmp;
} //内存错误调用警告 x1 ,没有if会出事
while (tmp->key != sth) {
if (tmp->key < sth) {
if (tmp->right == nullptr) {
return tmp;
}tmp = tmp->right; //这里没有用tmp->parent ,因为不同于STL的设计(其实是我想不出来),叶子节点没有两个 NULL型子节点
}
else {
if (tmp->left== nullptr) {
return tmp; //VS警告:返回局部变量或临时变量的地址:tmp //我...没想出来怎么解决这个警告,但是功能是对的
}tmp = tmp->left;
}
}return tmp;
delete(tmp);
}
node_ptr& nxt_mem(node_ptr st) { //后继: 比当前节点key 大一个单位的 节点
if (st->right)st = st->right; //方法: 向右走一个 , 然后一直向左走
else return st;
while (st->left) {
st = st->left;
}return st;
}
node_ptr& pre_mem(node_ptr st) { //前驱: 比当前节点key 小一个单位的 节点
if (st->left)st = st->left; //方法: 向左走一个 , 然后一直向右走
else return st;
while (st->right) {
st = st->right;
}return st;
}
void delete_node(Key key) {
node_ptr Res = Search(key); //定位
if (Res->key != key) {
cout << "the DATA didn't exist" << endl;
return;
}
if (size_counter == 1) { //如果就一个,直接删除root
size_counter--;
delete(Res); return;
}
if (Res->left==nullptr&&Res->right==nullptr) { //删除的地方是叶子
if (Res->parent->left == Res)Res->parent->left = nullptr; //断开叶子与父节点的链接
if (Res->parent->right == Res)Res->parent->right = nullptr;
delete(Res); size_counter--;
return;
}
else if (Res->left == nullptr) { //左子树为空,上下链接 “链”式删除
if (Res->parent) {
Res->parent->right = Res->right;
Res->right->parent = Res->parent;
}
else { //如果是root,更改header指针
header = Res->right;
}
delete(Res); size_counter--;
}
else if(Res->right == nullptr){ //对称的
if (Res->parent) {
Res->parent->left = Res->left;
Res->left->parent = Res->parent;
}
else {
header = Res->left;
}
delete(Res); size_counter--;
}
else { //左右都有子树
node_ptr tmp = nxt_mem(Res); //找到后继节点(比它大一点点的节点)
if (tmp->right) { //后继有右子树(因为是后继 所以没有左子树)
tmp->parent->left = tmp->right; //仿照删除“链”
tmp->right->parent = tmp->parent;
}
else {
if (tmp->parent->left == tmp)tmp->parent->left &#