节点的定义:
RBTreeNode.h
//
// Created by 24588 on 2022/1/12.
//
#ifndef TEST_RBTREENODE_H
#define TEST_RBTREENODE_H
enum RBColor
{
RED,BLACK
};
template <typename E>//E为可以比较的类型,< > == != =都要能用,如果传的是字典节点类型,那么就要重载运算符
struct RBTreeNode
{
RBTreeNode* left;
RBTreeNode* right;
RBTreeNode* parent;
E element;//可以比较的元素
RBColor col;
RBTreeNode(E e, RBColor c, RBTreeNode* l, RBTreeNode* r, RBTreeNode* p) :
element(e),col(c),left(l),right(r),parent(p){}
};
#endif //TEST_RBTREENODE_H
红黑树的定义:
RBTree.h
//
// Created by 24588 on 2022/1/12.
//
#ifndef TEST_RBTREE_H
#define TEST_RBTREE_H
#include <iostream>
#include "RBTreeNode.h"
using namespace std;
//RB1:根节点和所有外部节点都是黑色的
//RB2:在根至外部节点路径上,没有连续两个节点是红色
//RB3:在根至外部节点路径上,黑色节点的数目都相同
template <typename T>
class RBTree
{
public:
//构造方法与析构方法
RBTree() : m_root(nullptr){}
~RBTree() {clear();}
//ADT
bool isEmpty() const
{
return nullptr == m_root;
}
//外部接口
void preOrder()
{
preOrder(m_root);
}
void inOrder()
{
inOrder(m_root);
}
void postOrder()
{
postOrder(m_root);
}
void search(const T& element)
{
RBTreeNode<T>* target = search(element, m_root);
if (target == nullptr) cout << "not found" << endl;
else cout << target->element << endl;
}
const T& findMin() const
{
return findMin(m_root)->element;
}
const T& findMax() const
{
return findMax(m_root)->element;
}
void clear()
{
clear(m_root);
}
void insert(const T &x)
{
insert(x, m_root);
}
void erase(const T &x)
{
RBTreeNode<T>* node = search(x, m_root);//现在root中找x,返回x的指针
if (node == nullptr) return;//不存在要删除的节点
erase(node, m_root);
}
void predecessor(const T& element)
{
RBTreeNode<T>* target = search(element, m_root);
if (target == nullptr)
{
cout << "not found" << endl;
return;
}
else
{
cout << predecessor(target)->element << endl;
}
}
void successor(const T& element)
{
RBTreeNode<T>* target = search(element, m_root);
if (target == nullptr)
{
cout << "not found" << endl;
return;
}
else
{
cout << successor(target)->element << endl;
}
}
private://私有成员变量
RBTreeNode<T> *m_root;//RBTree的根节点
private://私有成员方法
RBTreeNode<T>* predecessor(RBTreeNode<T>* x);//找节点的前驱节点,值小于x中最大的那个
RBTreeNode<T>* successor(RBTreeNode<T>* x);//找节点的后驱节点,值大于x中最小的那个
//内部接口
void preOrder(RBTreeNode<T>* t, ostream& out = cout) const;
void inOrder(RBTreeNode<T>* t, ostream& out = cout) const;
void postOrder(RBTreeNode<T>* t, ostream& out = cout) const;
RBTreeNode<T>* search(const T& element,RBTreeNode<T>* root);
RBTreeNode<T>* findMin(RBTreeNode<T> *t) const;
RBTreeNode<T>* findMax(RBTreeNode<T> *t) const;
void clear(RBTreeNode<T>* &t);
void insert(const T &element, RBTreeNode<T>* &root);
void erase(RBTreeNode<T>* node, RBTreeNode<T>* &root);
//服务与insert与erase来保证红黑树平衡的方法
void rotateRight(RBTreeNode<T> *gu, RBTreeNode<T> *&root);
void rotateLeft(RBTreeNode<T> *gu, RBTreeNode<T> *&root);
void insertBlance(RBTreeNode<T> *node, RBTreeNode<T> *&root);
void eraseBlance(RBTreeNode<T> *cnode, RBTreeNode<T> *&root);
};
template <typename T>
void RBTree<T>::preOrder(RBTreeNode<T> *t, ostream &out) const
{
if (nullptr != t)
{
//out << t->element << ":" << t->col<<" ";//我用来看颜色画二叉树检验是不是红黑树的
out << t->element <<" ";
preOrder(t->left, out);
preOrder(t->right, out);
}
}
template <typename T>
void RBTree<T>::inOrder(RBTreeNode<T> *t, ostream &out) const
{
if (nullptr != t)
{
inOrder(t->left, out);
out << t->element << " ";
inOrder(t->right, out);
}
}
template <typename T>
void RBTree<T>::postOrder(RBTreeNode<T> *t, ostream &out) const
{
if (nullptr != t)
{
postOrder(t->left, out);
postOrder(t->right, out);
out << t->element << " ";
}
}
template <typename T>
RBTreeNode<T>* RBTree<T>::search(const T &element, RBTreeNode<T>* root)
{
while(root != nullptr && root->element != element)
{
root = element < root->element ? root->left : root->right;
}
return root;//找到范围节点的指针,没找到返回nullptr
}
template <typename T>
void RBTree<T>::clear(RBTreeNode<T> *&t)
{
if (t != nullptr)
{
clear(t->left);
clear(t->right);
delete t;
}
t = nullptr;
}
template <typename T>
RBTreeNode<T>* RBTree<T>::findMin(RBTreeNode<T> *t) const
{
if (t != nullptr)
while (t->left != nullptr)
t = t->left;
return t;
}
template <typename T>
RBTreeNode<T>* RBTree<T>::findMax(RBTreeNode<T> *t) const
{
if (t != nullptr)
while (t->right != nullptr)
t = t->right;
return t;
}
template <typename T>
RBTreeNode<T>* RBTree<T>::predecessor(RBTreeNode<T> *x)
{
if (x->left != nullptr)
return findMax(x->left);
RBTreeNode<T>* y = x->parent;
while (y != nullptr && y->left == x)
{
x = y;
y = y->parent;
}
return y;
}
template <typename T>
RBTreeNode<T>* RBTree<T>::successor(RBTreeNode<T> *x)//大于x的最小值
{
//1.若一个节点有右子树,那么该节点的后继节点是其右子树中值最小的节点
if (x->right != nullptr)//如果有右子树,就在这里找
return findMin(x->right);//右子树的都大于x,所以在右子树去找最小值
//2.1 若该节点是其父节点的左孩子,那么该节点的后继结点即为其父节点
//2.2 若该节点是其父节点的右孩子,那么需要沿着其父亲节点一直向树的顶端寻找,直到找到一个节点P,P节点是其父节点Q的左边孩子
RBTreeNode<T>* target = x->parent;//没有右子树只能向上找有右子树的父节点
while (target != nullptr && target->right == x)//x为右儿子,就一直往上,直到变成左孩子或者子节点指向根节点,退出
{
x = target;
target = target->parent;
}
return target;//如果没有进入循环,说明x作为左子树,比他大的最小值就是父亲
}
template <typename T>
void RBTree<T>::rotateRight(RBTreeNode<T> *gu, RBTreeNode<T> *&root)//针对LL型
{
//在AVL树的旋转方法基础上又加上了对父亲节点的修改
RBTreeNode<T>* fu = gu->left;//gu是祖父,fu是父亲
//步骤一:
gu->left = fu->right;//首先让gu的左指向fu的右
if (fu->right != nullptr)//先看fu的右是否存在
fu->right->parent = gu;//存在的话,把父亲改为gu,和上一步对应
//步骤二:将fu与gu的父亲联系起来
fu->parent = gu->parent;//fu的父亲改为gu的父亲,即fu变成新的祖父,顶替掉他的位置
if (gu->parent == nullptr)//当然如果gu的父亲本来是空的,即gu是根节点
root = fu;//fu就成为新的根节点
else//普通情况,gu不是根节点
{
//要去改gu的父节点的信息
if (gu == gu->parent->left)//gu是左节点,那gu父亲的左变为fu
gu->parent->left = fu;
else
gu->parent->right = fu;//gu是右节点,那gu父亲的右变为fu
}
//步骤三:
fu->right = gu;//fu的右变为gu
gu->parent = fu;//gu的父亲变为fu
}
template <typename T>
void RBTree<T>::rotateLeft(RBTreeNode<T> *gu, RBTreeNode<T> *&root)//针对RR型
{
//同rotateRight
RBTreeNode<T>* fu = gu->right;
gu->right = fu->left;
if (fu->left != nullptr)
fu->left->parent = gu;
fu->parent = gu->parent;
if (gu->parent == nullptr)
root = fu;
else
{
if (gu == gu->parent->left)
gu->parent->left = fu;
else
gu->parent->right = fu;
}
gu->parent = fu;
fu->left = gu;
}
//插入方法的平衡:因为默认插入红色节点,所以如果父节点是红色时,才会不平衡。
//要去关注叔叔节点(父节点的兄弟节点)的颜色:
//如果是红色的,那简单,只用改色,父节点和叔叔节点从红改为黑,然后祖父就从黑变为红,类似新的红色节点,循环处理
//如果是黑色(空的也算黑),麻烦些,你插入红色后,如果只把父亲改为黑,祖父改为红,会违背RB3,要利用旋转纠正,把父节点改为黑,祖父改为红后,旋转就成了
template <typename T>
void RBTree<T>::insertBlance(RBTreeNode<T> *node, RBTreeNode<T> *&root) {
//默认插入的红色节点
RBTreeNode<T> *parent = node->parent, *gparent = nullptr;//父节点与祖父节点
while (parent != nullptr && parent->col == RED)//父亲是红色时才可能不平衡,当然前提是父亲要有
{
gparent = parent->parent;//node的祖父节点
//分情况处理
if (parent == gparent->left)//如果父亲是左节点,初步判断是L类型不平衡
{
RBTreeNode<T> *uncle = gparent->right;//父亲节点的兄弟 叔叔节点uncle
if (uncle != nullptr && uncle->col == RED)//Lr类型,叔叔存在且是红色节点,只用改色就可以
{
//如果叔叔是红色,那爷爷肯定是黑色,,
parent->col = BLACK;//父亲从红改为黑
uncle->col = BLACK;//叔叔从红改为黑
gparent->col = RED;//祖父从黑色变为红色
node = gparent;//现在node指向gparent,往上去查,因为祖父颜色变了,可能会引发新的不平衡,如果祖父的父节点不是根节点
continue;
}
else //Rb类型,没有叔叔节点或者有但是个黑色节点,这要讨论旋转
{
if (node == parent->right)//如果是LRb类型,就先左旋,变成LLb类型
{
rotateLeft(parent, root);
//旋转后 node变成了父亲,parent变成了儿子,所以交换了一下
RBTreeNode<T> *tmp = parent;
parent = node;
node = tmp;
}
parent->col = BLACK;
gparent->col = RED;
rotateRight(gparent, root);//然后右旋
}
}
else if (parent == gparent->right)//如果父亲是左节点,初步判断是R类型不平衡
{
RBTreeNode<T> *uncle = gparent->left;
if (uncle != nullptr && uncle->col == RED)//Rr类型,只用改色
{
parent->col = BLACK;
uncle->col = BLACK;
gparent->col = RED;
node = gparent;
continue;
}
else //Rb类型,要旋转
{
if (node == parent->left)//如果是RLb类型,就先右旋,变成RRb类型
{
rotateRight(parent, root);
//旋转后 node变成了父亲,parent变成了儿子
RBTreeNode<T> *tmp = parent;
parent = node;
node = tmp;
}
parent->col = BLACK;
gparent->col = RED;
rotateLeft(gparent, root);//然后左旋
}
}
}
root->col = BLACK;//while循环过完后,tree是根节点
}
template <typename T>
void RBTree<T>::insert(const T & element, RBTreeNode<T> *&root)
{
RBTreeNode<T> *node = new RBTreeNode<T>(element, RED, nullptr, nullptr, nullptr);//用element构建的RB节点
RBTreeNode<T> *cur = root;//cur用来遍历root
RBTreeNode<T> *parent = nullptr;//newNode的父亲
//将红黑树当做一颗二叉搜索树去插入节点
if (root == nullptr) // 如果是个空树
{
root = node;
node->col = BLACK;//作为根节点,颜色改为黑色
}
else //root不是空树
{
while (cur != nullptr)//cur最终到空指针,parent是记录上一次,即父亲
{
parent = cur;
if (node->element < cur->element)
cur = cur->left;
else if (node->element > cur->element)
cur = cur->right;
else //if (node->element < cur->element)
return;//有现存的
}
node->parent = parent;
//现在找到了父亲,接下来判断插入在左边还是右边
if (node->element < parent->element)
parent->left = node;
else
parent->right = node;
//插入后可能不平衡,调用insertBlance函数判断并使平衡
insertBlance(node, root);
}
}
//eraseBlance方法的目的:给定node,创造出删掉node就平衡的状况
//具体步骤 看兄弟什么颜色:
//2.1 兄弟是红色:进行旋转涂色,去到兄弟为黑色那里处理
//2.2 兄弟是黑色,看看兄弟子节点是不是全部都是黑。
//(1)全黑的话,看父什么颜色进行对应处理;
//(2)不全黑,看兄在的位置,兄在左的话,看兄的左子是不是红色,进行对应处理;兄在右的话,看兄的右子是不是红色,进行对应处理。
template <typename T>
void RBTree<T>::eraseBlance(RBTreeNode<T> *node, RBTreeNode<T> *&root)//针对删除的是黑色叶子节点的平衡算法,就他最复杂
{
while(node != root && node->col == BLACK)
{
RBTreeNode<T> *parent = node->parent;
if (node == parent->left)
{
RBTreeNode<T> *brother = parent->right;
//先检查brother是红色的吗? 如果是,就:以parent进行左旋,然后交换parent和brother的颜色
//这样就变成了兄弟节点时黑色的情况
if (brother->col == RED)
{
parent->col = RED;
brother->col = BLACK;
rotateLeft(parent,root);
//旋转后,node的brother变了
brother = parent->right;
}
//讨论兄弟节点是黑色的情况
//情形1 兄弟的子节点都是空或者都是红(兄弟节点不可能有黑色非空孩子,因为node是黑色叶子节点)
if (brother->left == nullptr && brother->right == nullptr)
{
//情形1.1 父节点为红色 : 简单,直接修改brother的颜色,改为红,然后父亲改为黑,等会你再删掉node就平衡了
if (parent->col == RED)
{
brother->col = RED;
parent->col = BLACK;
break;
}
//情形1.2 父节点为黑色 : 复杂了一些,brother改为红后,父亲开始的树平衡了,但总体的树就不平衡了,要往上处理新的不平衡直到根节点,然后删去node
else //if (parent->col == BLACK)
{
brother->col = RED;
node = parent;
}
}
//情形2 兄弟的子节点一个红一个空
else
{
//情形2.1 左黑右红 : 以parent左旋,parent和brother互换颜色,然后brother的右节点从红改为黑
if (brother->left == nullptr || brother->left->col == BLACK)
{
brother->col = parent->col;
parent->col = BLACK;
brother->right->col = BLACK;
rotateLeft(parent,root);
break;
}
//情形2.2 左红右黑
else //if (brother->right->col == BLACK)
{
brother->left->col = BLACK;
brother->col = RED;
rotateRight(brother, root);
brother = parent->right;
//转换为了左黑右红类型,照抄左黑右红的代码
brother->col = parent->col;
parent->col = BLACK;
brother->right->col = BLACK;
rotateLeft(parent,root);
break;
}
}
}
else //if (node == parent->right)
{
RBTreeNode<T> *brother = parent->left;
//先检查brother是红色的吗? 如果是,就:以parent进行右旋,然后交换parent和brother的颜色
//这样就变成了兄弟节点是红色的情况
if (brother->col == RED)
{
parent->col = RED;
brother->col = BLACK;
rotateRight(parent,root);
//旋转后,node的brother变了
brother = parent->left;
}
//讨论兄弟节点是黑色的情况
//情形1 兄弟的子节点都是空或者都是红(兄弟节点不可能有黑色非空孩子,因为node是黑色叶子节点)
if (brother->left == nullptr && brother->right == nullptr)
{
//情形1.1 父节点为红色 : 简单,直接修改brother的颜色,改为红,然后父亲改为黑,等会你再删掉node就平衡了
if (parent->col == RED)
{
brother->col = RED;
parent->col = BLACK;
break;
}
//情形1.2 父节点为黑色 : 复杂了一些,brother改为红后,父亲开始的树平衡了,但总体的树就不平衡了,要往上处理新的不平衡直到根节点,然后删去node
else //if (parent->col == BLACK)
{
brother->col = RED;
node = parent;
}
}
//情形2 兄弟的子节点一个红一个空
else
{
//情形2.1 左黑右红 : 以parent左旋,parent和brother互换颜色,然后brother的右节点从红改为黑
if (brother->left == nullptr || brother->left->col == BLACK)
{
brother->col = parent->col;
parent->col = BLACK;
brother->right->col = BLACK;
rotateRight(parent,root);
break;
}
//情形2.2 左红右黑
else //if (brother->right == BLACK)
{
brother->left->col = BLACK;
brother->col = RED;
rotateLeft(brother, root);
brother = parent->right;
//转换为了左黑右红类型,照抄左黑右红的代码
brother->col = parent->col;
parent->col = BLACK;
brother->right->col = BLACK;
rotateRight(parent,root);
break;
}
}
}
}
}
template <typename T>
void RBTree<T>::erase(RBTreeNode<T> * node, RBTreeNode<T> *&root)
{
//分四种情况 左右都空,左有右空,左空右有,左有都有 只有删的是黑色节点才需要平衡
//最终目的是找真正要删的rnode,它有两种情况,什么都没有,或有一棵树
//node有两棵树时会转换目标为上面的情况,
//node是要删除的节点,rnode是真正要删的,因为删掉node不一定真的干掉,而是把他的值换成别的,然后删去原来那个
//cnode是rnode的顶替节点,其实就是孩子节点,把rnode删掉了,需要节点代替它的位置
RBTreeNode<T>* rnode = nullptr;
RBTreeNode<T>* cnode = nullptr;
if (node->left == nullptr && node->right == nullptr)
{
//若node左右没有子树,rnode = node直接删除
rnode = node;
if (rnode->col == RED)//如果是红色,直接删
{
if (rnode == rnode->parent->left)
rnode->parent->left = nullptr;
else
rnode->parent->right = nullptr;
delete rnode;
}
else //if (rnode->col == BLACK) //被删节点是黑色
{
if (rnode->parent == nullptr)//删的是根节点,这也不用考虑平衡了
{
root = nullptr;
delete rnode;
}
else//普通情况,被删节点是黑色而且无子节点,这个是最复杂的最难的
{
//RBTreeNode<T> * temp =
eraseBlance(rnode,root);
//平衡后还不是真的平衡,只是创造删掉rnode后才平衡的状况
//切断关系 并删除
if (rnode == rnode->parent->left)
rnode->parent->left = nullptr;
else
rnode->parent->right = nullptr;
delete rnode;
//这才真的平衡了
}
}
}
else if (node->left != nullptr && node->right == nullptr)
{
//若node只有一个子树,让子节点cnode代替node后(),rnode = node,删掉node
//这种情况下 node只能为黑,cnode为红
rnode = node;
cnode = node->left;
//修改rnode父节点与cnode的关系
cnode->parent = rnode->parent;
if (cnode->parent == nullptr)
root = cnode;
else if (rnode == rnode->parent->left)
rnode->parent->left = cnode;
else //if (rnode == rnode->parent->right)
rnode->parent->right = cnode;
cnode->col = BLACK;//cnode从红改为黑
delete rnode;
}
else if (node->left == nullptr && node->right != nullptr)
{
//若node只有一个子树,让子节点cnode代替node后(),rnode = node,删掉node
//这种情况下 node只能为黑,cnode为红
rnode = node;
cnode = node->right;
//修改rnode父节点与cnode的关系
cnode->parent = rnode->parent;
if (cnode->parent == nullptr)
root = cnode;
else if (rnode == rnode->parent->left)
rnode->parent->left = cnode;
else //if (rnode == rnode->parent->right)
rnode->parent->right = cnode;
cnode->col = BLACK;//cnode从红改为黑
delete rnode;
}
else if (node->left != nullptr && node->right != nullptr)
{
//若左右两棵树都有,则去左子树找前驱节点或去右子树找后继结点,两个都可以
//然后把这个节点的值赋给node,去删除这个节点
//默认去左边找前驱节点,rnode那么就有两种情况 什么都没有,或有左子树(不会有右子树,前驱节点就是一直往右走)
//情况就和上面类似了
rnode = predecessor(node);//找前驱节点
node->element = rnode->element;//node的值改为前驱节点的值,现在要把前驱节点删掉
erase(rnode,root);//把rnode当成新的node,等待它的只有无子树和只有左子树情况
}
}
//如果采用pair类型,重载的运算符
template <class K, class E>
ostream& operator<<(ostream& out, const pair<K,E> &x)
{
out << "(" << x.first << "," << x.second << ")";return out;
}
template <class K, class E>
bool operator<(const pair<K,E> &x, const pair<K,E> &y)
{
return x.first < y.first;
}
#endif //TEST_RBTREE_H
测试:
RBTree.cpp
//
// Created by 24588 on 2022/1/12.
//
#include <iostream>
#include "RBTree.h"
using namespace std;
int main()
{
int arr[]= {10, 40, 30, 60, 90, 70, 20, 50, 80};
int size = (sizeof(arr)) / (sizeof(arr[0])) ;
auto* tree=new RBTree<int>();
for(int i=0; i < size; i++)
{
tree->insert(arr[i]);
}
cout << "preOrder" ;tree->preOrder();cout << endl;
cout << "inOrder" ;tree->inOrder();cout << endl;
cout << "postOrder" ;tree->postOrder();cout << endl;
cout << "max : " << tree->findMin() << endl;
cout << "max : " << tree->findMax() << endl;
cout << "successor :" ;tree->successor(100);
cout << "successor :" ;tree->successor(50);
cout << "predecessor :" ;tree->predecessor(100);
cout << "predecessor :" ;tree->predecessor(50);
for (int i = 0 ; i < size; i++)
{
cout << "erase " << arr[i] << ":\t";
tree->erase(arr[i]);
tree->inOrder();cout << endl;
}
return 0;
}
结果
preOrder30 10 20 60 40 50 80 70 90
inOrder10 20 30 40 50 60 70 80 90
postOrder20 10 50 40 70 90 80 60 30
max : 10
max : 90
successor :not found
successor :60
predecessor :not found
predecessor :40
erase 10: 20 30 40 50 60 70 80 90
erase 40: 20 30 50 60 70 80 90
erase 30: 20 50 60 70 80 90
erase 60: 20 50 70 80 90
erase 90: 20 50 70 80
erase 70: 20 50 80
erase 20: 50 80
erase 50: 80
erase 80: