^ _ ^ 感谢各位大佬光临,I love You
mua~
红黑树是一种高级的数据结构
可以说红黑树是普通二叉搜索树的升级版本,可以自动平衡
此文章不对红黑树的原理进行解析,因为我太懒了~
文章底部将会插入一些红黑树原理的一些结构图(单旋转、双旋转)
此文章将以三个 ( .h 文件 ) 和 一个 ( .cpp文件 ) 来实现红黑树:
- Except.h (
异常类包含的头文件
) - Wrapper.h (
自定义的引用类型包含的头文件
) - RedBlackTreeH.h (
红黑树实现的头文件
) - Main.cpp (
包含main的头文件,用来测试红黑树
)
为了测试,所有成员都将以 public 展示
一、Except.h 实现
#pragma once
#include <string>
using namespace std;
class DSException // 作为异常类的基类
{
public:
DSEexception(const string& msg = "") : message(msg){}
virtual ~DSException(){}
virtual string toString() const
{
return "Exception " + string(": ") + what();
}
virtual string what() const
{
return message;
}
private:
string message;
};
// 有重复的异常类
class DuplicateItemException : public DSException
{
public:
DuplicateItemException(const string& msg = "“) : DSException(msg){}
};
// 不存在(为空)的异常类
class NullPointerException : public DSException
{
public:
NullPointerException(const string& msg = "") : DSException(msg){}
};
二、Wrapper.h 实现
其中将用到异常类,所以包含 Except.h 头文件
#pragma once
#include "Except.h"
template <class Object>
class Cref
{
public:
Cref() : obj(NULL){}
explicit Cref(const Object& x) : obj(&x){}
const Object & get() const
{
if (isNull()) // obj 是个空值 获取它的值将 引发异常
{
throw NullPointerException();
}
else
{
return *obj;
}
}
bool isNull() const
{
return obj == NULL;
}
private:
const Object* obj;
};
注: Cref 类只是对指针进行一个包装而已
三、RedBlackTreeH.h 实现
实现红黑树最基本的功能,其中包含最重要的动态平衡 insert方法
其中会用到异常类、引用类、和双端队列,所有将引入这些头文件
#pragma once
#include "Wrapper.h"
#include "Except.h"
#include <deque> // 层级遍历会用到 双端队列
// 声明红黑树和结点类
template <class T>
class RedBlackTree;
template <class T>
class RedBlackNode;
template <class T>
class RedBlackNode
{
public:
T value; // 数据值
RedBlackNode<T>* left; // 左孩子
RedBlackNode<T>* right; // 右孩子
int color; // 颜色
// 构造结点
RedBlackNode(const T& val = T(), RedBlackNode<T>* li = nullptr, RedBlackNode<T>* ri = nullptr,
int k = RedBlackTree<T>::Black) : value(val), left(li), right(ri), color(k) {}
friend class RedBlackTree<T>; // 使得红黑树 能够操控结点
};
template <class T>
class RedBlackTree // 红黑树
{
public:
enum { RED, BLACK }; // 枚举颜色
using Node = RedBlackNode<T>; // 取结点的别名
public:
Node* header; // 头结点
Node* nullNode; // 空结点
Node* current; // 当前结点
Node* parent; // 父结点
Node* grand; // 祖父结点
Node* great; // 曾祖父结点
public:
// 不是根的根 一个负值最大的数
RedBlackTree(const T& val); // 构造函数 创建伪根
~RedBlackTree();
public:
bool isEmpty(); // 判断是否为空
void makeEmpty(); // 释放树的内存
void levelPrint(); // 层级遍历
// 返回一个封装好的引用类型
Cref<T> findMin(); // 查找最小值
Cref<T> findMax(); // 查找最大值
Cref<T> find(const T& val) const; // 查找数据
public:
// ---红黑树核心部分
void Insert(const T& x); // 插入数据 自动平衡
void rotateWithLeftChild(Node*& k2) const; // 右侧旋转
void rotateWithRightChild(Node*& k1) const; // 左侧旋转
// 实现过程中,并不会使用该方法,只是了解一下原理
// void doubleRotateWithLeftChild(Node*& k3) const; // 向右双旋转 由单旋转实现
// void doubleRotateWithRightChild(Node*& k1) const; // 向左双旋转
// 重新调整红黑树 自动平衡功能实现
void handleReorient(const T& item);
// 通过父结点 判断 item的旋转情况
RedBlackNode<T>* rotate(const T& item, Node* theParent) const; // 将多种旋转可能封装在一起
void reclaimMemory(Node*& t) const; // 释放内存
void inPrint(Node* t); // 中序遍历
RedBlackNode<T>* remove(Node* root, T const& data); // 删除数据
};
template <class T>
RedBlackTree<T>::~RedBlackTree()
{
makeEmpty(); // 释放内存
delete header; // 释放伪根
delete nullNode; // 释放空结点
}
template <class T>
void RedBlackTree<T>::Insert(const T& x)
{
current = great = grand = parent = header;
nullNode->value = x;
// 找到合适的位置 树中有 x 还是 nullNode中的x
while(current->value != x)
{
great = grand; grand = parent; parent = current;
current = x < current->value ? current->left : current->right;
// 当前结点左右孩子都是红色的情况下 将发生旋转
if(current->left->color == RED && current->right->color == RED)
{
handlReoriet(x);
}
}
if(current != nullNode) // 插入了重复的数据 抛出异常
throw DuplicateItemException();
current = new Node(x, nullNode, nullNode); // current 的左右孩子都初始为 nullNode结点
// 挂载到 父结点上
if(parent->value > x)
parent->left = current;
else
parent->right = current;
handleReorient(x); // 判断是否旋转
}
template <class T>
void RedBlackTree<T>::rotateWithLeftChild(Node*& k2) const
{
Node* k1 = k2->left;
k2->left = k1->right; // 横向平移
k1->right = k2;
k2 = k1; // 旋转过后, k1 为根结点
}
template <class T>
void RedBlackTree<T>::rotateWithRightChild(Node*& k1) const
{
Node* k2 = k1->right;
k1->right = k2->left;
k2->left = k1;
k1 = k2;
}
// 双旋转 功能实现原理
//template <class T>
//void RedBlackTree<T>::doubleRotateWithLeftChild(Node*& k3) const
//{
// rotateWithRightChild(k3->left); // 先将 k3的左孩子向左移动
// rotateWithLeftChild(k3); // 再将 k3向右移动
//}
//template <class T>
//void RedBlackTree<T>::doubleRotateWithRightChild(Node*& k1) const
//{
// rotateWithLeftChild(k1->right);
// rotateWithRightChild(k1);
//}
// 非常重要一个方法实现
template <class T>
void RedBlackTree<T>::handleReorint(const T& item)
{
// 插入的结点是红色的 或者当前结点有两个红色的孩子
current->color = RED;
current->left->color = BLACK;
current->right->color = BLACK;
if(parent->color == RED)
{
grand->color = RED;
// 内孙子则多一次旋转 变成双旋转
if(item < grand->value != item < parent->value)
{
parent = rotate(item, grand);
}
current = rotate(item, great);
current->color = BLACK; // 父结点是红的,则叶子结点必须是黑的
}
header->right->color = BLACK; // 根结点必须是黑的
}
template <class T>
RedBlackNode<T>* RedBlackTree<T>::rotate(const T& item, Node* theParent) const
{
if(item < theParent->value) // 在父结点的左边
{
item < theParent->left ? // 判断在父结点左孩子的哪一边
rotateWithLeftChild(theParent->left) :
rotateWithRightChild(theParent->left);
return theParent->left;
}
else
{
item < theParent->right?
rotateWithLeftChild(theParent->right) :
rotateWithRightChild(theParent->right);
return theParent->right;
}
}
template <class T>
bool RedBlackTree<T>::isEmpty()
{
return header->right == nullNode; // 判断根结点是不是等于空结点
}
template <class T>
void RedBlackTree<T>::makeEmpty()
{
reclaimMemory(header->right);
header->right = nullNode; // 清空内容后 根结点指向空结点
}
template <class T>
void RedBlackTree<T>::reclaimMemory(Node*& t) const
{
if(t != t->left) // 最后空结点和左右孩子都是一样的
{
// 递归遍历每一个结点 释放内存
reclaimMemory(t->left);
reclaimMemory(t->right);
delete t;
}
}
template <class T>
void RedBlackTree<T>::inPrint(Node* t) // 中序遍历
{
if(t != nullNode)
{
inPrint(t->left);
cout << t->value << " ";
inPrint(t->right);
}
}
template <class T>
void RedBlackTree<T>::levelPrint() // 层级遍历
{
deque<Node*>* deq = new deque<Node*>; // 创建一个双端队列
Node* root = header->right;
deq->push-back(root);
while(!deq->empty())
{
root = deq->front(); // 获取队头的结点
cout << root->value << " ";
deq->pop_front(); // 队头的结点出列
if(root->left != nullNode)
{
deq->push_back(root->left);
}
if(root->right != nullNode)
{
deq->push_back(root->right);
}
}
}
template <class T>
RedBlackNode<T>* RedBlackTree<T>::remove(Node* root, T const& data)
{
if(root != nullNode)
{
// 找到要删除的结点
if(root->value > data)
{
root->left = remove(root->left, data);
return root;
}
else if(root->value < data)
{
root->right = remove(root->right, data);
return root;
}
else
{
// 判断要删除的结点是否左右孩子都存在
if(root->left != nullNode && root->right != nullNode)
{
Node* node = root->right; // 通过结点的右孩子 找到左边最小的结点
while(node->left != nullNode)
{
node = node->left;
}
root->value = node->value;
root->right = remove(root->right, root->value); // 从右孩子开始递归删除 一样的值
}
else
{
Node*& delNode = root;
if(root->left != nullNode)
{
root = root->left; // 接管内存
}
else if(root->right != nullNode)
{
root = root->right;
}
delete delNode; // 释放被删除的结点
delNode = nullNode;
}
return root;
}
}
}
template <class T>
Cref<T> RedBlackTree<T>::findMin()
{
if(isEmpty())
return Cref<T>(); // 返回一空引用
Node* itr = header->right;
while(itr->left != nullNode) // 找到最左边的叶子结点
{
itr = itr->left;
}
return Cref<T>(itr->value);
}
template <class T>
Cref<T> RedBlackTree<T>::findMax()
{
if(isEmpty())
return Cref<T>();
Node* itr = header->right;
while(itr->right != nullNode) // 找到最右边的叶子结点
{
itr = itr->right;
}
return Cref<T>(itr->value);
}
template <class T>
Cref<T> RedBlackTree<T>::find(const T& val) const
{
nullNode->value = val; // 空结点的值为 val 终止循环条件
Node* curr = header->right;
for(;;)
{
if(val < curr->value)
{
curr = curr->left;
}
else if(val > curr->value)
{
curr = curr->right;
}
else if(curr != nullNode) // 找到了数据
{
return Cref<T>(curr->value);
}
else // 找了整棵树,都没有找到 到空结点停止寻找
{
return Cref<T>();
}
}
}
四、Main.cpp 测试红黑树
#include <iostream>
#include "RedBlackTreeH.h"
int main()
{
const int NUM_MINUS_MAX = -99999;
// 创建一个包含伪根的红黑树
RedBlackTree<int>* t = new RedBlackTree<int>(NUM_MINUS_MAX);
// 插入数据
t->Insert(50);
t->Insert(40);
t->Insert(30);
t->Insert(59);
t->Insert(80);
cout << "中序遍历:" << endl;
t->inPrint(t->header->right); // 升序打印数据
cout << endl;
t->remove(t->header->right, 30); // 删除数据
cout << "中序遍历:" << endl;
t->inPrint(t->header->right); // 此时 30已经不存在
cout << endl;
if(t->findMax().get() == 80)
cout << "找到了最大值" << endl;
if(t->findMax().get() == 40) // 30被删除 40则为最小值
cout << "找到了最小值" << endl;
if(t->find(59).get() == 59)
{
cout << "找到了数据" << endl;
}
if(t->find(100).isNull())
{
cout << "没有找到数据" << endl;
}
delete t;
return 0;
}
运行结果为:
中序遍历:
30 40 50 59 80
中序遍历:
40 50 59 80
找到了最大值
找到了最小值
找到了数据
没有找到数据
其中一些代码需要加一些结构图来理解
右侧旋转与左侧旋转:##
双旋转结构图:
四种旋转情况:
插入结点产生旋转的几种可能性结构图:
看图理解的比较快哟~
谢谢大佬观看我的文章,小弟我三生有幸 ^ _ ^
来源:CSDN
作者:浪子花梦
链接:https://blog.csdn.net/weixin_42100963/article/details/104000427