使用一颗红黑树同时封装map和set。
红黑树源码
#pragma once
#include <cassert>
#include <iostream>
#include <utility>
using namespace std;
// 红黑树结点颜色
enum Colour {
RED,
BLACK,
};
template<class K, class V>
struct RBTreeNode {
//使用三叉链
RBTreeNode<K, V> *_left;
RBTreeNode<K, V> *_right;
RBTreeNode<K, V> *_parent;
pair<K, V> _kv;
Colour _col;//结点颜色
// 在单参数构造函数中使用 explicit 关键字是一种好的编程习惯,可以提高代码的可读性和健壮性。
// 加上 explicit 关键字,以避免出现不必要的隐式类型转换。如果没有加上 explicit 关键字,那么可以使用该构造函数创建一个 RBTreeNode 对象时,会发生隐式类型转换,将一个 pair<K, V> 类型的对象转换为 RBTreeNode 对象,这可能导致程序行为出现意外的结果。
explicit RBTreeNode(const pair<K, V> &kv)
: _left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _col(RED) {
}
};
template<class K, class V>
class RBTree {
typedef RBTreeNode<K, V> Node;
public:
// 析构函数
~RBTree() {
Destroy(this->_root);
this->_root = nullptr;
}
Node *find(const K &key) {
Node *cur = this->_root;
while (cur) {
if (key > cur->_kv.first) {
cur = cur->_right;
} else if (key < cur->_kv.first) {
cur = cur->_left;
} else {
return cur;
}
}
return nullptr;
}
bool Insert(const pair<K, V> &kv) {
//创建根结点
if (_root == nullptr) {
_root = new Node(kv);
_root->_col = BLACK;//根结点为黑色
return true;
}
Node *parent = nullptr;
Node *cur = _root;
//寻找结点插入位置
while (cur) {
if (kv.first > cur->_kv.first) {
parent = cur;
cur = cur->_right;
} else if (kv.first < cur->_kv.first) {
parent = cur;
cur = cur->_left;
} else {
// 相等则不插入
return false;
}
}
// cur走到了合适的位置
cur = new Node(kv);
// 选择插入到parent的左边还是右边
if (kv.first < parent->_kv.first) {
parent->_left = cur;
} else {
parent->_right = cur;
}
// cur链接parent
cur->_parent = parent;
// parent存在且parent的节点为红色的(意味着,循环往上调整到parent不存在或者parent为黑就不用调整了)
// 红黑树性质:红色结点的孩子必须是黑色的
while (parent && parent->_col == RED) {
Node *grandfather = parent->_parent;
// 如果爷爷的左边是父亲,那么爷爷的右边就是叔叔
if (grandfather->_left == parent) {
Node *uncle = grandfather->_right;
// 情况1:u存在且为红,变色处理,并继续往上处理
if (uncle && uncle->_col == RED) {
// 调整parent变黑,uncle变黑,grandfather变红
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
// 继续往上调整,此时的grandfather作为新插入结点,继续判断他的父亲是否是红色结点
// 重置parent,先将grandfather位置看成新增结点cur
cur = grandfather;
parent = cur->_parent;
} else {// 情况2+3 u不存在/u存在且为黑,旋转+变色
//插入结点是父亲的左孩子
if (cur == parent->_left) {
// g
// p u
// c
// g p c成为一条直线,并且cur在parent的左边
// 需要右旋+变色,右旋后parent成为根,需要变黑,grandfather变为parent的右孩子,需要变红
RotateRight(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
} else {
// g
// p u
// c
// 当cur为parent的右边时,需要左旋+右旋+变色
RotateLeft(parent);
RotateRight(grandfather);// 右旋cur成为新的根,变为黑色,grandfather变为cur孩子,变为红色
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
} else {//(grandfather->_right == parent) 如果爷爷的右边是父亲,那么爷爷的左边就是叔叔
// g
// u p
// c
Node *uncle = grandfather->_left;
// 情况1:u存在且为红,变色处理,并继续往上处理
if (uncle && uncle->_col == RED) {
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
// 继续往上调整
cur = grandfather;
parent = cur->_parent;
} else// 情况2+3:u不存在/u存在且为黑,旋转+变色
{
// g
// u p
// c
if (cur == parent->_right) {
RotateLeft(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
} else {
// g
// u p
// c
RotateRight(parent);
RotateLeft(grandfather);
//parent的位置没变不需要变色
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
// 最后的根变为黑节点
_root->_col = BLACK;
return true;
}
void InOrder() {
InOrder(this->_root);
}
bool IsBalance() {
if (_root && _root->_col == RED) {
cout << "根节点颜色是红色" << endl;
return false;
}
int benchmark = 0;// 基准值,任选一条做,用于比较每条节点黑色节点相同,如果不相同则说明不平衡
Node *cur = this->_root;
while (cur) {
if (cur->_col == BLACK)
benchmark++;
cur = cur->_left;
}
// 连续红色节点
return _check(this->_root, 0, benchmark);
}
int Height() {
return Height(this->_root);
}
private:
void Destroy(Node *root) {
if (root == nullptr) {
return;
}
// 后序销毁
Destroy(root->_left);
Destroy(root->_right);
delete root;
}
int Height(Node *root) {
if (root == nullptr)
return 0;
int leftHeight = Height(root->_left);
int rightHeight = Height(root->_right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
bool _check(Node *root, int BlackNum, int benchmark) {
// 检查不能存在连续的红色节点
// benchmark基准值
if (root == nullptr) {
if (benchmark != BlackNum) {
cout << "某条路径黑色节点的数量不相等" << endl;
return false;
}
return true;
}
if (root->_col == BLACK) {
BlackNum++;
}
if (root->_col == RED && root->_parent && root->_parent->_col == RED) {
cout << "存在连续的红色节点" << endl;
return false;
}
return _check(root->_left, BlackNum, benchmark) && _check(root->_right, BlackNum, benchmark);
}
void InOrder(Node *root) {
if (root == nullptr) {
return;
}
InOrder(root->_left);
cout << root->_kv.first << " ";
InOrder(root->_right);
}
// 左单旋
void RotateLeft(Node *parent) {
Node *subR = parent->_right;// 要旋转的parent的右子树
Node *subRL = subR->_left; // 子树的左子树
// 旋转链接
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
// 需要记录要旋转的树还有没有父亲
Node *ppnode = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
// 如果ppnode为nullptr,说明parent一开始为根,旋转后subR为根
if (ppnode == nullptr) {
// 更新根节点
_root = subR;
_root->_parent = nullptr;
} else {
if (ppnode->_left == parent) {
ppnode->_left = subR;
} else {
ppnode->_right = subR;
}
subR->_parent = ppnode;
}
}
// 右单旋
void RotateRight(Node *parent) {
Node *subL = parent->_left;
Node *subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
Node *ppnode = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (ppnode == nullptr) {
_root = subL;
_root->_parent = nullptr;
} else {
if (ppnode->_left == parent) {
ppnode->_left = subL;
} else {
ppnode->_right = subL;
}
subL->_parent = ppnode;
}
}
private:
Node *_root;
};
红黑树模板参数的控制
我们都知道,set是K模型的容器,而map是KV模型的容器,那我们如何用一棵KV模型的红黑树同时实现map和set呢?
这里我们就需要控制map和set传入底层红黑树的模板参数,为了与原红黑树的模板参数进行区分,我们将红黑树第二个模板参数的名字改为T。
template<class K, class T>
class RBTree
T模板参数可能只是键值Key,也可能是由Key和Value共同构成的键值对。如果是set容器,那么它传入底层红黑树的模板参数就是Key和Key:
template<class K>
class set{
public:
//...
private:
RBTree<K, K> _t;
};
但如果是map容器,那么它传入底层红黑树的模板参数就是Key以及Key和Value构成的键值对:
template<class K, class V>
class map{
public:
//...
private:
RBTree<K, pair<const K, V>> _t;
};
那能不能不要红黑树的第一个模板参数,只保留第二个模板参数呢?
乍眼一看好像是可以的,因为此时红黑树的第二个模板参数当中也是有键值Key的,但实际上红黑树的第一个模板参数是不能省略的。
对于set容器来说,省略红黑树的第一个参数当然没问题,因为set传入红黑树的第二个参数与第一个参数是一样的。但是对于map容器来说就不行了,因为map容器所提供的接口当中有些是只要求给出键值Key的,比如find和erase。
红黑树结点当中存储的数据
现在红黑树的模板参数变成了K和T,那么红黑树结点当中存储的应该是什么呢?
前面说到,由于上层容器的不同,底层红黑树当中的K和T也是不同的:
- set容器:K和T都代表键值Key。
- map容器:K代表键值Key,T代表由Key和Value构成的键值对。
对于set容器来说,底层红黑树结点当中存储K和T都是一样的,但是对于map容器来说,底层红黑树就只能存储T了。由于底层红黑树并不知道上层容器到底是map还是set,因此红黑树的结点当中直接存储T就行了。
这样一来,当上层容器是set的时候,结点当中存储的是键值Key;当上层容器是map的时候,结点当中存储的就是<Key, Value>键值对。
模板参数中仿函数的增加
现在由于结点当中存储的是T,这个T可能是Key,也可能是<Key, Value>键值对。那么当我们需要进行结点的键值比较时,应该如何获取结点的键值呢?
当上层容器是set的时候T就是键值Key,直接用T进行比较即可,但当上层容器是map的时候就不行了,此时我们需要从<Key, Value>键值对当中取出键值Key后,再用Key值进行比较。
因此,上层容器map需要向底层红黑树提供一个仿函数,用于获取T当中的键值Key,这样一来,当底层红黑树当中需要比较两个结点的键值时,就可以通过这个仿函数来获取T当中的键值了。
map中的仿函数:
template<class K, class V>
class map {
//用于比较方式
struct MapKeyOfT {
const K &operator()(const pair<const K, V> &key) {
return key.first;
}
};
private:
RBTree<K, pair<const K, V>, MapKeyOfT> _t;//const K 控制k不可修改 传给模板T
};
但是对于底层红黑树来说,它并不知道上层容器是map还是set,因此当需要进行两个结点键值的比较时,底层红黑树都会通过传入的仿函数来获取键值Key,进而进行两个结点键值的比较。
因此,set容器也需要向底层红黑树传入一个仿函数,虽然这个仿函数单独看起来没什么用,但却是必不可少的。
template<class K>
class set {
//用于比较方式
struct SetKeyOfT {
const K &operator()(const K &key) {
return key;
}
};
private:
RBTree<K, K, SetKeyOfT> _t;//封装的红黑树 ,第二个K是没用的,为的是匹配map
};
这样一来,当底层红黑树需要进行两个结点之间键值的比较时,都会通过传入的仿函数来获取相应结点的键值,然后再进行比较,下面以红黑树的查找函数为例:
Node *Find(const T &key) {
Node *cur = this->_root;
KeyOfT kot;
while (cur) {
if (key > kot(cur->_data)) {
cur = cur->_right;
} else if (key < kot(cur->_data)) {
cur = cur->_left;
} else {
return cur;
}
}
return nullptr;
}
注意: 所有进行结点键值比较的地方,均需要通过仿函数获取对应结点的键值后再进行键值的比较。
正向迭代器的实现
红黑树的正向迭代器实际上就是对结点指针进行了封装,因此在正向迭代器当中实际上就只有一个成员变量,那就是正向迭代器所封装结点的指针。
template<class T, class Ref, class Ptr>
struct __RBTreeIterator {
typedef RBTreeNode<T> Node;
typedef __RBTreeIterator<T, Ref, Ptr> Self;
Node *_node;// 正向迭代器所封装结点的指针
};
因此,我们通过一个结点的指针便可以构造出一个正向迭代器。
//构造函数
__RBTreeIterator(Node *node)
: _node(node)
}
当对正向迭代器进行解引用操作时,我们直接返回对应结点数据的引用即可。
Ref operator*() {
return _node->_data;
}
当对正向迭代器进行->
操作时,我们直接返回对应结点数据的指针即可。
Ptr operator->(){
return &_node->_data;
}
当然,正向迭代器当中至少还需要重载==
和!=
运算符,实现时直接判断两个迭代器所封装的结点是否是同一个即可。
bool operator!=(const Self &s) {
return _node != s._node;
}
bool operator==(const Self &s) const {
return _node == s._node;//判断两个正向迭代器所封装的结点是否是同一个
}
红黑树正向迭代器实现时,真正的难点实际上++
和--
运算符的重载。
实现红黑树的正向迭代器时,一个结点的正向迭代器进行++操作后,应该根据红黑树中序遍历的序列找到当前结点的下一个结点。
具体逻辑如下:
- 如果当前结点的右子树不为空,则++操作后应该找到其右子树当中的最左结点。
- 如果当前结点的右子树为空,则++操作后应该在该结点的祖先结点中,找到孩子不在父亲右的祖先。
代码如下:
Self &operator++() {
if (_node->_right) {
// 1、右不为空,下一个就是右子树的最左节点
Node *subLeft = _node->_right;
while (subLeft->_left) {
subLeft = subLeft->_left;
}
_node = subLeft;
} else {
// 2、右为空,沿着到根的路径,找孩子是父亲左的那个祖先
Node *cur = _node;
Node *parent = cur->_parent;
// 如果parent不为NULL,或者cur是parent的右子树
while (parent && cur == parent->_right) {
// 继续走
cur = parent;
parent = parent->_parent;
}
_node = parent;// 下一个节点就是parent
}
return *this;
}
实现红黑树的正向迭代器时,一个结点的正向迭代器进行--
操作后,应该根据红黑树中序遍历的序列找到当前结点的前一个结点。
具体逻辑如下:
- 如果当前结点的左子树不为空,则
--
操作后应该找到其左子树当中的最右结点。 - 如果当前结点的左子树为空,则
--
操作后应该在该结点的祖先结点中,找到孩子不在父亲左的祖先。
代码如下:
// 反向 右子树 根 左子树
Self &operator--() {
//++找左子树,--找右子树
if (_node->_left) {
// 1、左不为空,找左子树的最右节点
Node *subRight = _node->_left;
while (subRight->_right) {
subRight = subRight->_right;
}
_node = subRight;
} else {
// 2、左为空,找孩子是父亲右的祖先
Node *cur = _node;
Node *parent = cur->_parent;
// 如果parent!=null ,cur==parent的左就继续走
while (parent && cur == parent->_left) {
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
正向迭代器实现后,我们需要在红黑树的实现当中进行迭代器类型的typedef。需要注意的是,为了让外部能够使用typedef后的正向迭代器类型iterator,我们需要在public区域进行typedef。
然后在红黑树当中实现成员函数begin和end:
- begin函数返回中序序列当中第一个结点的正向迭代器,即最左结点。
- end函返回中序序列当中最后一个结点下一个位置的正向迭代器,这里直接用空指针构造一个正向迭代器。
template<class K, class T, class KeyOfT>
class RBTree {
typedef RBTreeNode<T> Node;
public:// 迭代器相关
typedef __RBTreeIterator<T, T &, T *> iterator;
typedef __RBTreeIterator<T, const T &, const T *> const_iterator;
// 迭代器最开始应该是树的最左边(中序)
iterator begin() {
Node *cur = this->_root;
while (cur && cur->_left) {
cur = cur->_left;
}
return iterator(cur);
}
iterator end() {
return iterator(nullptr);
}
const_iterator begin() const {
Node *cur = _root;
while (cur && cur->_left) {
cur = cur->_left;
}
return const_iterator(cur);
}
const_iterator end() const {
return const_iterator(nullptr);
}
private:
Node *_root;//红黑树的根结点
};
在C++STL中,底层红黑树实现正向迭代器时所用的结构。
实际上,上述所实现的迭代器是有缺陷的,因为理论上我们对end()
位置的正向迭代器进行--
操作后,应该得到最后一个结点的正向迭代器,但我们实现end()
时,是直接返回由nullptr构造得到的正向迭代器的,因此上述实现的代码无法完成此操作。
下面我们来看看C++SLT库当中的实现逻辑:
C++STL库当中实现红黑树时,在红黑树的根结点处增加了一个头结点,该头结点的左指针指向红黑树当中的最左结点,右指针指向红黑树当中的最右结点,父指针指向红黑树的根结点。
在该结构下,实现begin()时,直接用头结点的左孩子构造一个正向迭代器即可,实现rbegin()时,直接用头结点的右孩子构造一个反向迭代器即可(实际是先用该结点构造一个正向迭代器,再用正向迭代器构造出反向迭代器),而实现end()和rend()时,直接用头结点构造出正向和反向迭代器即可。此后,通过对逻辑的控制,就可以实现end()进行–操作后得到最后一个结点的正向迭代器。
但实现该结构需要更改当前很多函数的逻辑,例如插入结点时,若插入到了红黑树最左结点的左边,或最右结点的右边,此时需要更新头结点左右指针的指向,这里就不进行实际实现了。
反向迭代器的实现
红黑树的反向迭代器实际上就是正向迭代器的一个封装,因此红黑树的反向迭代器就是一个迭代器适配器。
在反向迭代器当中只有一个成员变量,那就是反向迭代器封装的正向迭代器。反向迭代器的中成员函数,都是通过调用正向迭代器对应的函数来完成相应功能的。
//反向迭代器---迭代器适配器
template<class Iterator>
struct ReverseIterator {
typedef ReverseIterator<Iterator> Self; //反向迭代器的类型
typedef typename Iterator::reference Ref;//结点指针的引用
typedef typename Iterator::pointer Ptr; //结点指针
Iterator _it;//反向迭代器所封装的正向迭代器
//构造函数
ReverseIterator(Iterator it)
: _it(it)//根据所给正向迭代器构造一个反向迭代器
{}
Ref operator*() {
return *_it;//通过调用正向迭代器的operator*返回结点数据的引用
}
Ptr operator->() {
return _it.operator->();//通过调用正向迭代器的operator->返回结点数据的指针
}
//前置++
Self &operator++() {
--_it;//调用正向迭代器的前置--
return *this;
}
//前置--
Self &operator--() {
++_it;//调用正向迭代器的前置++
return *this;
}
bool operator!=(const Self &s) const {
return _it != s._it;//调用正向迭代器的operator!=
}
bool operator==(const Self &s) const {
return _it == s._it;//调用正向迭代器的operator==
}
};
需要注意的是,反向迭代器只接收了一个模板参数,即正向迭代器的类型,也就是说,反向迭代器不知道结点的引用类型和结点的指针类型,因此我们需要在正向迭代器当中对这两个类型进行typedef,这样反向迭代器才能通过正向迭代器获取结点的引用类型和结点的指针类型。
//正向迭代器
template<class T, class Ref, class Ptr>
struct __TreeIterator{
typedef Ref reference; //结点指针的引用
typedef Ptr pointer; //结点指针
};
反向迭代器实现后,我们也需要在红黑树的实现当中进行迭代器类型的typedef,并在红黑树当中实现成员函数rbegin和rend:
- rbegin函数返回中序序列当中最后一个结点的反向迭代器,即最右结点。
- rend函数返回中序序列当中第一个结点前一个位置的反向迭代器,这里直接用空指针构造一个反向迭代器。
template<class K, class T, class KeyOfT>
class RBTree {
typedef RBTreeNode<T> Node;//结点的类型
public:
typedef ReverseIterator<iterator> reverse_iterator;//反向迭代器
reverse_iterator rbegin() {
//寻找最右结点
Node *right = _root;
while (right && right->_right) {
right = right->_right;
}
//返回最右结点的反向迭代器
return reverse_iterator(iterator(right));
}
reverse_iterator rend() {
//返回由nullptr构造得到的反向迭代器(不严谨)
return reverse_iterator(iterator(nullptr));
}
private:
Node *_root;//红黑树的根结点
};
红黑树改造完整代码
//RBTree.h
#pragma once
#include <iostream>
#include <utility>
using namespace std;
// 红黑树节点颜色
enum Colour {
RED,
BLACK,
};
template<class T>
struct RBTreeNode {
RBTreeNode<T> *_left;
RBTreeNode<T> *_right;
RBTreeNode<T> *_parent;
T _data;
Colour _col;
// 在单参数构造函数中使用 explicit 关键字是一种好的编程习惯,可以提高代码的可读性和健壮性。
// 加上 explicit 关键字,以避免出现不必要的隐式类型转换。如果没有加上 explicit 关键字,那么可以使用该构造函数创建一个 RBTreeNode 对象时,会发生隐式类型转换,将一个 pair<K, V> 类型的对象转换为 RBTreeNode 对象,这可能导致程序行为出现意外的结果。
RBTreeNode(const T &data)
: _left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _col(RED) {}
};
// 迭代器
template<class T, class Ref, class Ptr>
struct __RBTreeIterator {
typedef Ref reference;//结点指针的引用
typedef Ptr pointer; //结点指针
typedef RBTreeNode<T> Node;
typedef __RBTreeIterator<T, Ref, Ptr> Self;
Node *_node;// 成员变量
__RBTreeIterator(Node *node)
: _node(node) {
}
// 根据RBTree的模板实例化传参,构造出普通迭代器和const迭代器
// 1、typedef __RBTreeIterator<T, T&, T*> itertaor; 拷贝构造
// 2、typedef __RBTreeIterator<T, const T&, const T*> const_itertaor;
// 支持普通迭代器构造const迭代器的构造函数
// 用普通模板T 构造出const类型的
__RBTreeIterator(const __RBTreeIterator<T, T &, T *> &it)
: _node(it._node) {
}
// Ref为引用
Ref operator*() {
return _node->_data;
}
// Ptr指针
Ptr operator->() {
return &_node->_data;
}
bool operator!=(const Self &s) {
return _node != s._node;
}
bool operator==(const Self &s) const {
return _node == s._node;//判断两个正向迭代器所封装的结点是否是同一个
}
Self &operator++() {
if (_node->_right) {
// 1、右不为空,下一个就是右子树的最左节点
Node *subLeft = _node->_right;
while (subLeft->_left) {
subLeft = subLeft->_left;
}
_node = subLeft;
} else {
// 2、右为空,沿着到根的路径,找孩子是父亲左的那个祖先
Node *cur = _node;
Node *parent = cur->_parent;
// 如果parent不为NULL,或者cur是parent的右子树
while (parent && cur == parent->_right) {
// 继续走
cur = parent;
parent = parent->_parent;
}
_node = parent;// 下一个节点就是parent
}
return *this;
}
// 反向 右子树 根 左子树
Self &operator--() {
//++找左子树,--找右子树
if (_node->_left) {
// 1、左不为空,找左子树的最右节点
Node *subRight = _node->_left;
while (subRight->_right) {
subRight = subRight->_right;
}
_node = subRight;
} else {
// 2、左为空,找孩子是父亲右的祖先
Node *cur = _node;
Node *parent = cur->_parent;
// 如果parent!=null ,cur==parent的左就继续走
while (parent && cur == parent->_left) {
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
};
//反向迭代器---迭代器适配器
template<class Iterator>
struct ReverseIterator {
typedef ReverseIterator<Iterator> Self; //反向迭代器的类型
typedef typename Iterator::reference Ref;//结点指针的引用
typedef typename Iterator::pointer Ptr; //结点指针
Iterator _it;//反向迭代器所封装的正向迭代器
//构造函数
ReverseIterator(Iterator it)
: _it(it)//根据所给正向迭代器构造一个反向迭代器
{}
Ref operator*() {
return *_it;//通过调用正向迭代器的operator*返回结点数据的引用
}
Ptr operator->() {
return _it.operator->();//通过调用正向迭代器的operator->返回结点数据的指针
}
//前置++
Self &operator++() {
--_it;//调用正向迭代器的前置--
return *this;
}
//前置--
Self &operator--() {
++_it;//调用正向迭代器的前置++
return *this;
}
bool operator!=(const Self &s) const {
return _it != s._it;//调用正向迭代器的operator!=
}
bool operator==(const Self &s) const {
return _it == s._it;//调用正向迭代器的operator==
}
};
// 仿函数,用于pair的比较
template<class K, class T, class KeyOfT>
class RBTree {
typedef RBTreeNode<T> Node;//结点的类型
public:
// 析构函数
~RBTree() {
_Destroy(_root);
_root = nullptr;
}
public:// 迭代器相关
typedef __RBTreeIterator<T, T &, T *> iterator;
typedef __RBTreeIterator<T, const T &, const T *> const_iterator;
typedef ReverseIterator<iterator> reverse_iterator; //反向迭代器
typedef ReverseIterator<const_iterator> reverse_const_iterator;//反向迭代器
// 迭代器最开始应该是树的最左边(中序)
iterator begin() {
Node *cur = this->_root;
while (cur && cur->_left) {
cur = cur->_left;
}
return iterator(cur);
}
iterator end() {
return iterator(nullptr);
}
const_iterator begin() const {
Node *cur = _root;
while (cur && cur->_left) {
cur = cur->_left;
}
return const_iterator(cur);
}
const_iterator end() const {
return const_iterator(nullptr);
}
reverse_iterator rbegin() {
//寻找最右结点
Node *right = _root;
while (right && right->_right) {
right = right->_right;
}
//返回最右结点的反向迭代器
return reverse_iterator(iterator(right));
}
reverse_iterator rend() {
//返回由nullptr构造得到的反向迭代器(不严谨)
return reverse_iterator(iterator(nullptr));
}
reverse_const_iterator rbegin() const {
//寻找最右结点
Node *right = _root;
while (right && right->_right) {
right = right->_right;
}
//返回最右结点的反向迭代器
return reverse_const_iterator(const_iterator(right));
}
reverse_const_iterator rend() const {
//返回由nullptr构造得到的反向迭代器(不严谨)
return reverse_const_iterator(const_iterator(nullptr));
}
public:
Node *Find(const T &key) {
Node *cur = this->_root;
KeyOfT kot;
while (cur) {
if (key > kot(cur->_data)) {
cur = cur->_right;
} else if (key < kot(cur->_data)) {
cur = cur->_left;
} else {
return cur;
}
}
return nullptr;
}
pair<iterator, bool> Insert(const T &data) {
if (_root == nullptr) {
_root = new Node(data);
_root->_col = BLACK;
return make_pair(iterator(_root), true);
}
KeyOfT kot;// 用于map和set的比较方式,KeyOft在上层传值,一个是key,一个是pair
Node *parent = nullptr;
Node *cur = _root;
while (cur) {
if (kot(data) > kot(cur->_data)) {
parent = cur;
cur = cur->_right;
} else if (kot(data) < kot(cur->_data)) {
parent = cur;
cur = cur->_left;
} else {
// 相等则不插入
return make_pair(iterator(cur), false);
}
}
// cur走到了合适的位置
cur = new Node(data);
Node *newnode = cur;// 用于返回插入节点
// 选择插入到parent的左边还是右边
if (kot(data) < kot(parent->_data)) {
parent->_left = cur;
} else {
parent->_right = cur;
}
// cur链接parent
cur->_parent = parent;
// parent存在且parent的节点为红色的(意味着,循环往上调整到parent不存在或者parent为黑就不用调整了)
while (parent && parent->_col == RED) {
Node *grandfather = parent->_parent;
// 如果爷爷的左边是父亲,那么爷爷的右边就是叔叔
if (grandfather->_left == parent) {
Node *uncle = grandfather->_right;
// 情况1:u存在且为红,变色处理,并继续往上处理
if (uncle && uncle->_col == RED) {
// 调整parent变黑,uncle变黑,grandfather变红
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
// 继续网上调整
// 重置parent,先将grandfather位置看成新增结点cur
cur = grandfather;
parent = cur->_parent;
} else// 情况2+3 u不存在/u存在且为黑,旋转+变色
{
if (cur == parent->_left) {
// g
// p u
// c
// 如果cur在parent的左边,需要右旋+变色,右旋后parent成为根,需要变黑,grandfather变为parent的孩子,需要变红
RotateRight(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
} else {
// g
// p u
// c
// 当cur为parent的右边时,需要左旋+右旋+变色
RotateLeft(parent);
RotateRight(grandfather);// 右旋cur成为新的根,变为黑色,grandfather变为cur孩子,变为红色
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
} else//(grandfather->_right == parent) 如果爷爷的右边是父亲,那么爷爷的左边就是叔叔
{
// g
// u p
// c
Node *uncle = grandfather->_left;
// 情况1:u存在且为红,变色处理,并继续往上处理
if (uncle && uncle->_col == RED) {
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
// 继续往上调整
cur = grandfather;
parent = cur->_parent;
} else// 情况2+3:u不存在/u存在且为黑,旋转+变色
{
// g
// u p
// c
if (cur == parent->_right) {
RotateLeft(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
} else {
// g
// u p
// c
RotateRight(parent);
RotateLeft(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
// 最后的根变为黑节点
_root->_col = BLACK;
return make_pair(iterator(newnode), true);
}
void InOrder() {
_InOrder(this->_root);
}
bool IsBalance() {
if (_root && _root->_col == RED) {
cout << "根节点颜色是红色" << endl;
return false;
}
int benchmark = 0;
Node *cur = _root;
while (cur) {
if (cur->_col == BLACK)
++benchmark;
cur = cur->_left;
}
// 连续红色节点
return _Check(_root, 0, benchmark);
}
int Height() {
return Height(this->_root);
}
private:
void _Destroy(Node *root) {
if (root == nullptr) {
return;
}
// 后序销毁
_Destroy(root->_left);
_Destroy(root->_right);
delete root;
}
int _Height(Node *root) {
if (root == nullptr)
return 0;
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
bool check(Node *root, int BlackNum, int benchmark) {
// 检查不能存在连续的红色节点
// benchmark基准值
if (root == nullptr) {
if (benchmark != BlackNum) {
cout << "某条路径黑色节点的数量不相等" << endl;
return false;
}
return true;
}
if (root->_col == BLACK) {
BlackNum++;
}
if (root->_col == RED && root->_parent && root->_parent->_col == RED) {
cout << "存在连续的红色节点" << endl;
return false;
}
return check(root->_left, BlackNum, benchmark) && check(root->_right, BlackNum, benchmark);
}
void _InOrder(Node *root) {
if (root == nullptr) {
return;
}
KeyOfT kot;
_InOrder(root->_left);
cout << kot(root->_data) << " ";
_InOrder(root->_right);
}
// 左单旋
void RotateLeft(Node *parent) {
Node *subR = parent->_right;// 要旋转的parent的右子树
Node *subRL = subR->_left; // 子树的左子树
// 旋转链接
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
// 需要记录要旋转的树还有没有父亲
Node *ppnode = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
// 如果ppnode为nullptr,说明parent一开始为根,旋转后subR为根
if (ppnode == nullptr) {
// 更新根节点
_root = subR;
_root->_parent = nullptr;
} else {
if (ppnode->_left == parent) {
ppnode->_left = subR;
} else {
ppnode->_right = subR;
}
subR->_parent = ppnode;
}
}
// 右单旋
void RotateRight(Node *parent) {
Node *subL = parent->_left;
Node *subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
Node *ppnode = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (parent == _root) {
_root = subL;
_root->_parent = nullptr;
} else {
if (ppnode->_left == parent) {
ppnode->_left = subL;
} else {
ppnode->_right = subL;
}
subL->_parent = ppnode;
}
}
private:
Node *_root = nullptr;
};
set的模拟实现
完成上述操作后,set的模拟实现也就很简单了,其中的成员函数都是调用底层红黑树的对应接口就行了,只不过需要注意将插入函数和查找函数返回值当中的结点指针改为迭代器即可。
#include "RBTree.h"
namespace phw {
template<class K>
class set {
//用于比较方式
struct SetKeyOfT {
const K &operator()(const K &key) {
return key;
}
};
public:
//typename告诉编译器iterator是一个类型,而不是一个成员变量
typedef typename RBTree<K, K, SetKeyOfT>::iterator iterator;
typedef typename RBTree<K, K, SetKeyOfT>::reverse_iterator reverse_iterator;//反向迭代器
iterator begin() {
return _t.begin();
}
iterator end() {
return _t.end();
}
reverse_iterator rbegin() {
return _t.rbegin();
}
reverse_iterator rend() {
return _t.rend();
}
pair<iterator, bool> Insert(const K &key) {
return _t.Insert(key);
}
//删除函数
void erase(const K &key) {
_t.Erase(key);
}
//查找函数
iterator find(const K &key) {
return _t.Find(key);
}
void InOrder() {
_t.InOrder();
}
private:
RBTree<K, K, SetKeyOfT> _t;//封装的红黑树 ,第二个K是没用的,为的是匹配map
};
}// namespace phw
map的模拟实现
map的模拟实现也是相同的道理,其成员函数也是调用底层红黑树的对应接口,但对于map来说,除了将插入函数和查找函数返回值当中的结点指针改为迭代器之外,还需要实现[]
运算符的重载函数。
#pragma once
#include "RBTree.h"
namespace phw {
template<class K, class V>
class map {
//用于比较方式
struct MapKeyOfT {
const K &operator()(const pair<const K, V> &key) {
return key.first;
}
};
public:
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
typedef typename RBTree<K, pair<K, V>, MapKeyOfT>::reverse_iterator reverse_iterator;//反向迭代器
iterator begin() {
return _t.begin();
}
iterator end() {
return _t.end();
}
reverse_iterator rbegin() {
return _t.rbegin();
}
reverse_iterator rend() {
return _t.rend();
}
V &operator[](const K &key) {
pair<iterator, bool> ret = _t.Insert(make_pair(key, V()));
return ret.first->second;//first是迭代器
}
pair<iterator, bool> Insert(const pair<const K, V> &kv) {
return _t.Insert(kv);
}
//删除函数
void erase(const K &key) {
_t.Erase(key);
}
//查找函数
iterator find(const K &key) {
return _t.Find(key);
}
void InOrder() {
_t.InOrder();
}
private:
RBTree<K, pair<const K, V>, MapKeyOfT> _t;//const K 控制k不可修改 传给模板T
};
}// namespace phw
测试代码:
#include "map.h"
#include "set.h"
int main() {
phw::set<int> s;
s.Insert(1);
s.Insert(5);
s.Insert(3);
s.Insert(8);
s.Insert(8);
s.InOrder();//1 3 5 8
phw::map<string, int> map;
map["a"] = 1;
map["b"] = 2;
map["c"] = 3;
map["d"] = 4;
map["e"] = 5;
map.InOrder();// a b c d e
return 0;
}