前言
在上篇当中我们为了 让红黑树适用于 set 和 map 对红黑树进行了修改,还是实现了红黑树的迭代器,因为 set 和 map 的底层都是使用 红黑树,那么,set 和 map 的迭代器也就实现了。具体可以看本博客的上篇:C++ - map 和 set 的模拟实现上篇 - 红黑树当中的仿函数 - 红黑树的迭代器实现-CSDN博客
set 和 map 实现(下)
set 的 const 迭代器
要实现 set 和 map 的const 迭代器,就要先实现 红黑树当中的const 迭代器:
修改如下所示:
template<class T, class Ptr, class Ref>
struct __TreeIterator
{
typedef RBTreeNode<T> Node;
typedef __TreeIterator<T , Ptr, Ref> Self;
Node* _node;
__TreeIterator(Node* node)
:_node(node)
{}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
bool operator!=(const Self& s)
{
return _node != s._node;
}
Self& operator--();
Self& operator++()
{
// 此时就是最简单的情况
// 直接找出该结点的右子树的最小结点
if (_node->_right)
{
// 右树的最左节点(最小节点)
Node* subLeft = _node->_right;
while (subLeft->_left)
{
subLeft = subLeft->_left;
}
_node = subLeft;
}
else //_node->_left
{
Node* cur = _node;
Node* parent = cur->_parent;
// 找孩子是父亲左的那个祖先节点,就是下一个要访问的节点
while (parent)
{
if (cur == parent->_left)
{
break; // 说明已经找到,parent此时就是下一次需要迭代的结点
}
// 如果程序走到这里,该结点和 父亲结点的左右子树都遍历完了
// 就要往上迭代
// 直到找到 父亲 的右子树没有找完的情况
else //cur == parent->_right
{
cur = cur->_parent;
parent = parent->_parent;
}
}
_node = parent;
}
return *this;
}
};
// 红黑树节点的定义
template<class K, class T, class KeyOfT>
struct RBTree
{
typedef RBTreeNode<T> Node;
public:
typedef __TreeIterator<T , T* , T&> iterator;
typedef __TreeIterator<T , const T*, const T&> const_iterator;
// const_iterator
iterator begin()
{
Node* leftMin = _root;
// 加上 subleft 这个条件是为了防止 这棵树是空
while (leftMin && leftMin->_left)
{
leftMin = leftMin->_left;
}
return iterator(leftMin);
}
iterator end()
{
// end()不用像上述一样 找最大值
// 通过迭代器当中的 operator++()函数我们知道,中序最后都是遍历到 nullptr 的
// 这个 nullptr就是 根结点的父亲指针指向的 nullptr
return iterator(nullptr);
}
// const 迭代器
const_iterator begin() const
{
Node* leftMin = _root;
// 加上 subleft 这个条件是为了防止 这棵树是空
while (leftMin && leftMin->_left)
{
leftMin = leftMin->_left;
}
return iteconst_iteratorrator(leftMin);
}
const_iterator end() const
{
// end()不用像上述一样 找最大值
// 通过迭代器当中的 operator++()函数我们知道,中序最后都是遍历到 nullptr 的
// 这个 nullptr就是 根结点的父亲指针指向的 nullptr
return iterconst_iteratorator(nullptr);
}
··············
············
············
private:
Node* _root = nullptr;
};
上述就是 实现了 红黑树的 迭代器 和 const 迭代器。
set 的 const 迭代器:
// 如果 set 当中可以要修改的话应该像如下一样写
//typedef typename RBTree<K, K, SetKeyOfT>::iterator iterator;
// 下一个是 只对外开放 const 迭代器
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;
iterator begin()
{
return _t.begin();
}
iterator end()
{
return _t.end();
}
const_iterator begin() const
{
return _t.begin();
}
const_iterator end() const
{
return _t.end();
}
private:
RBTree<K, K, SetKeyOfT> _t;
但是如果单独 像上述一样在 set 当中这样实现 const 迭代器的话,是会出现问题的:编译报错:
error C2440: “return”: 无法从“__TreeIterator<T,T *,T &>”转换为“__TreeIterator<T,const T *,const T &>”
这是因为,上述 const_iterator 迭代器,的 begin()和 end()函数,用的是一个 普通对象 来使用红黑树当中的迭代器,就不行。
如果用普通对象来调用的话,在红黑树那一层,调用的就是 普通的迭代器,然后在 set 当中的 begin()和 end()返回的就是 以 普通的红黑树迭代器,但是 begin()和 end()返回类似是一个 const_iterator 类型,这里就发生了类型的转换。
在红黑树当中 实现的 const-iterator 和 普通的迭代器使用的是一个类模板, 是传入不同的参数,实例化出的不同类型。
库当中的实现就比较聪明:
如上是 库当中的set 的 const 迭代器 的begin 和 end 函数,发现,两个函数的返回值是 iterator 而不是 const_iterator。
然后我们修改代码 ,把 返回值为 const_iterator 的begin()函数和 end()函数删除掉,只留上面的两个函数,发现编译就通过了:
iterator begin() const
{
return _t.begin();
}
iterator end() const
{
return _t.end();
}
而且,也可以实现外部不能修改的 const 迭代器的功能。
加上const 修饰 this指针之后,这个 _t 对象就是 const 的对象了,那么调用的 begin()和 end()函数就是 const 版本的 函数。
虽然上述 begin 和 end()两个函数的 返回值 只是普通的 迭代器,但是在 set 当中是把 const 迭代器 typedef 出 iterator 和 const_iterator 两个迭代器名。然后不管在外面创建的是 iterator 还是 const_iterator 的迭代器,创建的都会是 coust_iterator:
我们这里只提供 const 版本的迭代器就可以适用 set 的使用场景了,因为set 当中只存储了 key ,这个 key 是不允许修改的。而且,const 对象可以使用 const 迭代器,这是权限的等价;普通对象也可以调用 const 迭代器,这是 权限的缩小;权限只能相等或者是 缩小,不能放大。
map 当中的迭代器
map 的 当中的修改规则是,取出的迭代器可以 修改 value 但是不能修改 key。那么我们该如何实现上述 效果呢?
如果我们使用 和在 set 当中的 一样的方式,只开放 const 迭代器的方式,这样的话,key 和 value 都不能修改了。
所以,我们肯定不能使用 set 当中的方式来实现。
其实在 上篇当中我们已经说明了:
C++ - map 和 set 的模拟实现上篇 - 红黑树当中的仿函数 - 红黑树的迭代器实现-CSDN博客
map 的传给 RBTree 的value 值不是一个简单的 value值,而且是一个 包含键值对的 pair 类。
而在 这个 pair 当中,我能发现 key 是const 修饰的,而 T(value)是普通类型。所以才实现 key 不能修改,value 可以修改。
更具体的细节可以去看 上篇当中的 对于 set 和 map 的实现介绍。
在 map 的成员当中会有一个 红黑树,这个红黑树的模版参数如下所示:
库当中的实现是非常巧妙的,红黑树当中每一个结点当中的 pair 对象是可以进行修改的,但是pair 当中的 key 是const 修饰的,所以 key 是不能修改的,但是 可以利用可以修改的 pair 对 其中的 value 进行修改。
两者的 operator--()函数
operator--()函数,和 operator++()函数是反过来来的,也就是说,如果一直--,那么输出结果是 跟中序结果相反的。遍历过程也就是 右子树 -> 根 -> 左子树 这样来遍历的。
operator--()和 operator++()两者在遍历和实现过程当中是完全对称的。
具体细节可以对照着 上篇当中对 operator++()函数介绍来理解:
C++ - map 和 set 的模拟实现上篇 - 红黑树当中的仿函数 - 红黑树的迭代器实现-CSDN博客
代码实现:
Self& operator--()
{
// 如果此时 右子树还有结点,就去找右子树当中找到最左边的结点
if (_node->_left)
{
Node* subleft = _node;
while (subleft)
{
subleft = subleft->_left;
}
_node = subleft;
}
// 如果 右边不为空
else
{
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left)
{
cur = cur->_parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
库当中的迭代器增加了一个 头结点:
让头结点 header 的 左指针指向 最左结点(最小结点),让右指针直系那个 最右结点(最右结点)。这样做可以快速的找到树当中的最大和最小的结点。
所以,咋库当中,红黑树的 begin()是这样写的:
返回的是 leftmost 函数的返回值。这样做,begin()相对于我们实现的begin()要相对高效一点,但是也高效不了多少,因为 红黑树是控制 了高度 的。
而 end()就是 hearder,因为end()是最右结点的下一个,本来是空,这里就是 hearder了。
header 还让 其 _parent 指针指向 根,让 根的 _parent 指向 header。
那么也就是说,it 指向 15 结点,it++ 之后,就应该指向 header 了。 因为15 的右子树为空,就要从父亲开始往上寻找某一个祖父,如果某一个祖父的右子树不为空,就访问这个祖父,如果没有就一直访问,直到访问到 根结点。那么此时就有头结点,就会访问到 header了。
最后就会是这种情况:
当然加了哨兵位之后,就有一些特殊情况,如下述举例:
上述如果按照我们之前找到的 cur == parent->_right 的话,就会死循环。
map 的 operator[]()函数
要实现这个函数,就要复用 insert()函数,但是这个函数,我们在红黑树当中实现的时候,返回值是 bool 类型,所以我们需要把 insert()函数,返回一个 pair<iterator,bool> 对象,这个对象当中的 迭代器就是 insert()当前插入的结点的迭代器。修改如下:
// RBTree.h
pair<iterator, bool> Insert(const T& data)
{
// 搜索二叉树的插入逻辑
//
// 如果当前树为空,直接用头指针只想新结点
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK;
return make_pair(iterator(_root),true);
}
// 不为空接着走
Node* parent = nullptr; // 用于首次插入时候指针的迭代
Node* cur = _root;
KeyOfT kot;
while (cur)
{
// 如果当前新插入的 key 值比 当前遍历的结点 key 值大
if (kot(cur->_data) < kot(data))
{
// 往右迭代器
parent = cur;
cur = cur->_right;
}
// 反之
else if (kot(cur->_data) > kot(data))
{
parent = cur;
cur = cur->_left;
}
else
{
// 如果相等,就不插入,即插入失败
return make_pair(iterator(cur),false);
}
}
// 此时已经找到 应该插入的位置
cur = new Node(data);
Node* newnode = cur;
cur->_col = RED;
// 再次判断大小,判断 cur应该插入到 parent 的那一边
if (kot(parent->_data) < kot(data))
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
// 链接 新插入结点 cur 的_parent 指针
cur->_parent = parent;
// 红黑树调整高度(平衡高度)的逻辑
while (parent && parent->_col == RED)
{
// parent 为 红,parent->_parent 一定不为空
Node* grandfather = parent->_parent;
// 如果父亲是在 祖父的左
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
// u存在且为红
if (uncle && uncle->_col == RED)
{
// 变色
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
// 继续向上处理
cur = grandfather;
parent = cur->_parent;
}
else // u不存在 或 存在且为黑
{
if (cur == parent->_left)
{
// g
// p
// c
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
// g
// p
// c
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;// 不需要再往上更新
}
}
else // parent == grandfather->_right
{
Node* uncle = grandfather->_left;
// u存在且为红
if (uncle && uncle->_col == RED)
{
// 变色
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
// 继续向上处理
cur = grandfather;
parent = cur->_parent;
}
else// 不存在 或者 存在且为黑色
{
if (cur == parent->_right)
{
// g
// p
// c
RotateL(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
}
else
{
// g
// p
// c
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
// 不管上述如何修改,红黑树的根结点永远是黑的
// 所以我们这里既直接硬性处理
_root->_col = BLACK;
return make_pair(iterator(newnode), true);
}
那么,在set 和map 当中都是复用了 红黑树当中的 insert()函数,所以,此时我们要把 set 和 map 当中的 insert()函数进行修改:
// MySet.h
// 这里的 iterator 是 const_iterator
pair<iterator, bool> insert(const K& key)
{
// 因为底层是哟个红黑树实现的,直接套用红黑树的 插入
// pair<iterator, bool> 其中的 iterator 是 普通的 iterator
return _t.Insert(key);
}
// mymap.h
pair<iterator, bool> insert(const pair<const K, V>& key)
{
// 因为底层是哟个红黑树实现的,直接套用红黑树的 插入
return _t.Insert(key);
}
但是此时有报错:
error C2440: “return”: 无法从“std::pair<__TreeIterator<T,T *,T &>,bool>”转换为“std::pair<__TreeIterator<T,const T *,const T &>,bool>”
这是因为 ,_t 是普通对象,所以调用的是一个普通的 迭代器,但是在 insert()函数当中的返回值 pair<iterator,bool> 当中的 iterator 是 set 当中对 const_iterator 的 typedef:
所以,此处就会发生 从 普通迭代器到 const 迭代器的转换。就会报错。
解决方法:
库当中是这样实现的:
使用 typename 来在 编译之后再去寻找这个迭代器。
而且,发现库当中对于 返回值 pair<iterator,bool> 当中的迭代器构造,是直接使用 first 成员进行构造的。
那么,我们就按照库当中的实现来实现:
因为 make_pair ()是自己推导类型,那么推导出来的 iterator 还是 set 当中的 typydef 的 const 迭代器,所以,在库当中就 用一个变量来接收 insert()函数返回的 pair 对象,而我们看到 这个 ret 的类型pair 的模版参数非常的复杂,请看下述分析:
具体请看上图当中的分析,但是在上述的书写之后,还是编译报错:
我们先来看 此时的 pair 的构造函数:
对于,pair 的构造函数,传入的第一个参数是 iterator x ,这个x 的类型是 iterator ,就是 RBTree当中的 iterator;但是 pair 当中 first成员的类型是 pair 类模板的第一个参数,是 iterator类型,但是这个 iterator 不是 RBTree 当中的 iterator,是 set 当中 typedef 出来的,其实这个 iterator是 const_iterator,所以在 初始化列表当中 first(x),就会发生类型的转换 从 RBTree 当中的 iterator 转换为 const_iterator,但是,我们没有写拷贝构造函数,所以这里是浅拷贝,只是一个 iterator 的浅拷贝,所以我们需要实现 拷贝构造函数来实现 深拷贝。
但是按理来说,一般实现的迭代器是不用实现拷贝构造函数,因为其中就只有一个 指针,我们对这个指针和一些功能函数进行了封装,我们不写,编译器自己生成的拷贝构造进行的浅拷贝也够迭代器的拷贝了。
如下图当中,说库当中实现的拷贝构造函数:
看上去他也是实现了 浅拷贝,直接把 iterator 当中的 node 指针赋值了。
但是仔细看这个拷贝构造函数的参数,是 iterator 类型的,如果是拷贝构造函数的话,此处传入的对象应该是 迭代器的类型,也就是说 上图当中的 self 这个 typedef 出来的类型,但是发现,却给出的是 iterator这个 typedef 出来的类型,我们仔细观察 iterator 这个模版类型参数,感觉还有点奇怪。
其实,普通迭代器可以构造 const 迭代器是因为,const 迭代器实现了拷贝构造函数,这个构造函数可以传入参数为 普通迭代器类型,然后构造一个 const 的迭代器。
这里的self 就是这个迭代器,iterator不是迭代器。我们观察iterator 这个模版类型参数,发现和我们在写 iterator 的模版参数是一样的:
所以说,这个 iterator 就是一个 普通的迭代器的模版参数,这个iterator 就是一个 普通迭代器,而self 是一个 可以 融合 普通迭代器和 const 迭代器的模版参数。
也就是说,这里不管在 拷贝构造函数当中传入的是一个 const 的迭代器还是一个 普通的迭代器,都会被 iterator 当中的模版参数修改为普通迭代器。
那么,此时,如果 这个类实例化出来是一个 普通迭代器,那么这就只是以 普通迭代器 的浅拷贝的拷贝构造函数;如果,这个类实例化出来是一个 const 迭代器,那么这个函数就是 const 迭代器的 构造函数。这个构造函数就支持 一个 普通迭代器 构造出一个 const 迭代器。
那么,此时,如果这个类模板示例化出来是一个 const 的迭代器,那么,在这个 const 迭代器当中就有两个构造函数,那么就相当于 const 迭代器当中没有拷贝构造吗?我们之前也说过,拷贝构造函数,是C++ 6 大默认函数之一,我们不写编译器会自动生成一个, 这里这个 又是 拷贝构造函数 又是 构造函数的函数,只是一个模版,是契合 普通迭代器 和 const 迭代器的模版,这个函数在 这 两种迭代器当中实现了不同的功能;但是,在普通迭代器当中实现的浅拷贝功能其实是多余的,因为拷贝构造函数我们不写,编译器会帮我们自己生成一个浅拷贝的拷贝构造函数。
整体,红黑树, map ,set 的三个文件完整代码:
// MyMap.h
#pragma once
#include"MyRBTree.h"
namespace Mynamespace
{
template<class K, class V>
class map
{
struct MapKeyOfT
{
const K& operator()(const pair<K, K>& kv)
{
return kv.first;
}
};
public:
// 如果 set 当中可以要修改的话应该像如下一样写
//typedef typename RBTree<K, K, SetKeyOfT>::iterator iterator;
// 下一个是 只对外开放 const 迭代器
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;
iterator begin()
{
return _t.begin();
}
iterator end()
{
return _t.end();
}
const_iterator begin() const
{
return _t.begin();
}
const_iterator end() const
{
return _t.end();
}
V& operator[](const K& key)
{
// 这里的 V()是 V 的匿名对象,如果是set 的就是 key 的构造函数,如果是 map 的就是 其中pair 的构造函数
pair<iterator, bool> ret = insert(make_pair(key, V()));
return ret.first->second;
}
pair<iterator, bool> insert(const pair<const K, V>& key)
{
// 因为底层是哟个红黑树实现的,直接套用红黑树的 插入
return _t.Insert(key);
}
private:
RBTree<K, pair<const K, V>, MapKeyOfT> _t;
};
}
// MySet.h
#pragma once
#include"MyRBTree.h"
namespace Mynamespace
{
template<class K>
class set
{
struct SetKeyOfT
{
// 和 map 对照的标准写法
//const k& operator()(const pair<K, K>& kv)
//{
// return p.first;
//}
// 其实可以直接这样写
const K& operator()(const K& key)
{
return key;
}
};
public:
// 如果 set 当中可以要修改的话应该像如下一样写
//typedef typename RBTree<K, K, SetKeyOfT>::iterator iterator;
// 下一个是 只对外开放 const 迭代器
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;
iterator begin() const
{
return _t.begin();
}
iterator end() const
{
return _t.end();
}
// 这里的 iterator 是 const_iterator
pair<iterator, bool> insert(const K& key)
{
// 因为底层是哟个红黑树实现的,直接套用红黑树的 插入
// pair<iterator, bool> 其中的 iterator 是 普通的 iterator
pair<typename RBTree<K, K, SetKeyOfT>::iterator, bool> ret = _t.Insert(key);
return pair<iterator, bool>(ret);
}
private:
RBTree<K, K, SetKeyOfT> _t;
};
}
// MyBRTree.h
#pragma once
#include<iostream>
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;
RBTreeNode(const T& data)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _data(data)
, _col(RED)
{}
};
template<class T, class Ptr, class Ref>
struct __TreeIterator
{
typedef RBTreeNode<T> Node;
typedef __TreeIterator<T , Ptr, Ref> Self;
typedef __TreeIterator<T, T*, T&> iterator;
Node* _node;
__TreeIterator(Node* node)
:_node(node)
{}
__TreeIterator(const iterator& it)
{
_node = it._node;
}
Ref operator*() const
{
return _node->_data;
}
Ptr operator->() const
{
return &_node->_data;
}
bool operator!=(const Self& s) const
{
return _node != s._node;
}
Self& operator--()
{
// 如果此时 右子树还有结点,就去找右子树当中找到最左边的结点
if (_node->_left)
{
Node* subleft = _node;
while (subleft)
{
subleft = subleft->_left;
}
_node = subleft;
}
// 如果 右边不为空
else
{
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left)
{
cur = cur->_parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
Self& operator++()
{
// 此时就是最简单的情况
// 直接找出该结点的右子树的最小结点
if (_node->_right)
{
// 右树的最左节点(最小节点)
Node* subLeft = _node->_right;
while (subLeft->_left)
{
subLeft = subLeft->_left;
}
_node = subLeft;
}
else //_node->_left
{
Node* cur = _node;
Node* parent = cur->_parent;
// 找孩子是父亲左的那个祖先节点,就是下一个要访问的节点
// cur == parent->_left说明已经找到,parent此时就是下一次需要迭代的结点
while (parent && cur == parent->_right)
{
cur = cur->_parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
};
// set->RBTree<K, K, SetKeyOfT> _t;
// map->RBTree<K, pair<K, V>, MapKeyOfT> _t;
// 红黑树节点的定义
template<class K, class T, class KeyOfT>
struct RBTree
{
typedef RBTreeNode<T> Node;
public:
typedef __TreeIterator<T , T* , T&> iterator;
typedef __TreeIterator<T , const T*, const T&> const_iterator;
// const_iterator
iterator begin()
{
Node* leftMin = _root;
// 加上 subleft 这个条件是为了防止 这棵树是空
while (leftMin && leftMin->_left)
{
leftMin = leftMin->_left;
}
return iterator(leftMin);
}
iterator end()
{
// end()不用像上述一样 找最大值
// 通过迭代器当中的 operator++()函数我们知道,中序最后都是遍历到 nullptr 的
// 这个 nullptr就是 根结点的父亲指针指向的 nullptr
return iterator(nullptr);
}
// const 迭代器
const_iterator begin() const
{
Node* leftMin = _root;
// 加上 subleft 这个条件是为了防止 这棵树是空
while (leftMin && leftMin->_left)
{
leftMin = leftMin->_left;
}
return const_iterator(leftMin);
}
const_iterator end() const
{
// end()不用像上述一样 找最大值
// 通过迭代器当中的 operator++()函数我们知道,中序最后都是遍历到 nullptr 的
// 这个 nullptr就是 根结点的父亲指针指向的 nullptr
return const_iterator(nullptr);
}
Node* Find(const K& key)
{
Node* cur = _root;
KeyOfT kot;
while (cur)
{
if (kot(cur->_data) < key)
{
cur = cur->_right;
}
else if (kot(cur->_data) > key)
{
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);
}
// 不为空接着走
Node* parent = nullptr; // 用于首次插入时候指针的迭代
Node* cur = _root;
KeyOfT kot;
while (cur)
{
// 如果当前新插入的 key 值比 当前遍历的结点 key 值大
if (kot(cur->_data) < kot(data))
{
// 往右迭代器
parent = cur;
cur = cur->_right;
}
// 反之
else if (kot(cur->_data) > kot(data))
{
parent = cur;
cur = cur->_left;
}
else
{
// 如果相等,就不插入,即插入失败
return make_pair(iterator(cur),false);
}
}
// 此时已经找到 应该插入的位置
cur = new Node(data);
Node* newnode = cur;
cur->_col = RED;
// 再次判断大小,判断 cur应该插入到 parent 的那一边
if (kot(parent->_data) < kot(data))
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
// 链接 新插入结点 cur 的_parent 指针
cur->_parent = parent;
// 红黑树调整高度(平衡高度)的逻辑
while (parent && parent->_col == RED)
{
// parent 为 红,parent->_parent 一定不为空
Node* grandfather = parent->_parent;
// 如果父亲是在 祖父的左
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
// u存在且为红
if (uncle && uncle->_col == RED)
{
// 变色
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
// 继续向上处理
cur = grandfather;
parent = cur->_parent;
}
else // u不存在 或 存在且为黑
{
if (cur == parent->_left)
{
// g
// p
// c
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
// g
// p
// c
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;// 不需要再往上更新
}
}
else // parent == grandfather->_right
{
Node* uncle = grandfather->_left;
// u存在且为红
if (uncle && uncle->_col == RED)
{
// 变色
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
// 继续向上处理
cur = grandfather;
parent = cur->_parent;
}
else// 不存在 或者 存在且为黑色
{
if (cur == parent->_right)
{
// g
// p
// c
RotateL(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
}
else
{
// g
// p
// c
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
// 不管上述如何修改,红黑树的根结点永远是黑的
// 所以我们这里既直接硬性处理
_root->_col = BLACK;
return make_pair(iterator(newnode), true);
}
void RotateL(Node* parent)
{
Node* cur = parent->_right; // 存储 parent 的右孩子
Node* curleft = cur->_left; // 存储 cur 的左孩子
parent->_right = curleft;
if (curleft) // 判断 cur 的左孩子是否为空
{
curleft->_parent = parent; // 不为空就 修改 cur 的左孩子的_parent 指针
}
cur->_left = parent;
// 留存一份 根结点指针
Node* ppnode = parent->_parent;
parent->_parent = cur;
// 如果parent 是根结点
if (parent == _root)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = cur;
}
else
{
ppnode->_right = cur;
}
cur->_parent = ppnode;
}
}
void RotateR(Node* parent)
{
Node* cur = parent->_left;
Node* curRight = cur->_right;
parent->_left = curRight;
if (curRight)
{
curRight->_parent = parent;
}
cur->_right = parent;
Node* ppnode = parent->_parent;
parent->_parent = cur;
if (parent == _root)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = cur;
}
else
{
ppnode->_right = cur;
}
cur->_parent = ppnode;
}
}
bool CheckColor(Node* root, int blacknum, int benchamark)
{
// 当走到叶子结点的 null 指针处,也就是 NIL结点处
if (root == nullptr)
{
// 如果计算出的路径黑色结点长度 和 外部计算的不一样
// 说明不是红黑树
if (blacknum != benchamark)
{
cout << "路径黑色结点个数不一样" << endl;
return false;
}
return true;
}
// 用于递归计算 路径的黑色结点个数
if (root->_color == BLACK)
blacknum++;
// 如果当前结点为 红色,且当前结点的父亲也是红色,就不是红黑树
if (root->_parent && root->_parent->_color == RED && root->_color == RED)
{
cout << "有连续红色" << endl;
return false;
}
// 左右子树递归
return CheckColor(root->_left, blacknum, benchamark)
&& CheckColor(root->_right, blacknum, benchamark);
}
// 外部调用接口
bool isBalance()
{
return isBalance(_root);
}
// 内部封装函数
bool isBalance(Node* root)
{
if (root == nullptr)
return true;
// 如果整棵树的 根结点不是 黑色的就不是红黑树
if (root->_color != BLACK)
{
cout << "根结点不是黑色" << endl;
return false;
}
// 基准值
// 在递归外部计算出左路第一条路径的 黑色结点值
int benchmark = 0;
Node* cur = root;
while (cur)
{
if (cur->_color == BLACK)
benchmark++;
cur = cur->_left;
}
return CheckColor(root, 0, benchmark);
}
private:
Node* _root = nullptr;
};