我们之前在学习 set 和 map 基本使用时就介绍了 set 和 map 底层都是红黑树,但这里有一个问题 – set 是K模型的容器,而 map 是KV模型的容器,那它们底层为什么能同样都使用红黑树呢?我们可以通过阅读源码来解答这个问题。(本文中使用的源码为 SGI 的 stl30 版本)
//map
#include
#include
#include
//set
#include
#include
#include
可以看到,stl map 和 set 头文件中分别包含了三个头文件,其中 stl_tree.h 是红黑树,stl_map.h 和 stl_set.h 是 map 和 set,而 stl_multimap.h 和 stl_multiset.h 则是 multimap 和 multiset;这就是为什么我们使用 multiset 和 multimap 时只需要包 set 和 map 头文件的原因。
//stl_map.h
template <typename _Key, typename _Tp, typename _Compare = std::less<_Key>,typename _Alloc = std::allocator<std::pair<const _Key, _Tp> > >
class map
{
public:
typedef _Key key_type;
typedef std::pair<const _Key, _Tp> value_type;
private:
typedef _Rb_tree<key_type, value_type, _Select1st<value_type>,
key_compare, _Pair_alloc_type> _Rep_type;
_Rep_type _M_t;
public:
pair<iterator, bool> insert(const value_type& __x) { return _M_t._M_insert_unique(__x); }
size_type erase(const key_type& __x) { return _M_t.erase(__x); }
iterator find(const key_type& __x) { return _M_t.find(__x); }
};
//stl_set.h
template<typename _Key, typename _Compare = std::less<_Key>, typename _Alloc = std::allocator<_Key> >
class set
{
public:
typedef _Key key_type;
typedef _Key value_type;
private:
typedef _Rb_tree<key_type, value_type, _Identity<value_type>,
key_compare, _Key_alloc_type> _Rep_type;
_Rep_type _M_t;
public:
pair<iterator, bool> insert(const value_type& __x)
{
std::pair<typename _Rep_type::iterator, bool> __p =
_M_t._M_insert_unique(__x);
return std::pair<iterator, bool>(__p.first, __p.second);
}
size_type erase(const key_type& __x) { return _M_t.erase(__x); }
iterator find(const key_type& __x) { return _M_t.find(__x); }
};
可以看到,set 和 map 的插入删除查找等各种功能都是封装复用的红黑树的接口,对于 map来说,key_type 就是我们平常所理解的 K,但是 value_type 却是 pair 类型,它的两个参数分别是模板参数 _Key 和 _Tp,也就是我们认为的传递给 map 的 K 和 V;
而对于 set 来说,key_type 也是我们平时认为的 K,但是我们发现 set 中居然还有一个变量 value_type,并且 value_type 的类型就是 key_type 的类型,但是 set 不是K模型的容器吗?它里面为什么要设计一个 value_type 变量呢?要解答这个问题,我们还得阅读红黑树的源码。
//stl_tree.h
//红黑树的基类
struct _Rb_tree_node_base
{
typedef _Rb_tree_node_base* _Base_ptr;
typedef const _Rb_tree_node_base* _Const_Base_ptr;
_Rb_tree_color _M_color;
_Base_ptr _M_parent;
_Base_ptr _M_left;
_Base_ptr _M_right;
};
//红黑树的节点结构
template<typename _Val>
struct _Rb_tree_node : public _Rb_tree_node_base
{
typedef _Rb_tree_node<_Val>* _Link_type;
_Val _M_value_field;
};
//红黑树
template<typename _Key, typename _Val, typename _KeyOfValue, typename _Compare, typename _Alloc = allocator<_Val> >
class _Rb_tree
{
protected:
typedef _Rb_tree_node_base* _Base_ptr;
public:
typedef _Key key_type;
typedef _Val value_type;
typedef value_type* pointer;
typedef value_type& reference;
typedef _Rb_tree_node<_Val>* _Link_type;
};
可以看到,红黑树中定义了一个基类 _Rb_tree_node_base,基类中定义了节点的颜色和三叉链结构,然后让 _Rb_tree_node 去继承基类,并在类中增加成员变量 _M_value_field, _M_value_field 的类型是 _val,而这个 _val 恰好是红黑树的第二个模板参数,也就是 map 和 set 传递过来的 value_type,如下:
通过这张图相信大家就可以很容易理解为什么 set 和 map 虽然是不同模型的容器,但都可以使用 红黑树 作为底层容器了 – 如果是 map,则红黑树的模板参数 _Val 被实例化为 pair
也就是说,红黑树完全可以根据第二个模板参数 _Val 的实参类型的不同实例化出符合 set 要求和 符合 map 要求的不同的两个类。到这里有的同学可能会疑惑,既然 _Val 就能区别出 set 和 map,那为什么还要传递第一个模板参数 _Key 呢?这是因为某些函数需要使用 key,比如 find 函数就只能通过 key 进行查询,即在 map 的 find 中不能通过 _Val 即 pair 类型来进行查询。
在明白了 stl 源码是如何解决 set K模型 和 map KV模型 的问题后,我们就可以参照它的做法将我们自己实现的红黑树改造一下:
//节点颜色
enum Color {
RED,
BLACK
};
template<class T>
struct RBTreeNode {
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
T _data;
Color _col; //节点颜色
RBTreeNode(const T& data)
: _data(data)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _col(RED) //默认给成红色
{}
};
template<class K, class T>
class RBTree {
typedef RBTreeNode<T> Node;
public:
bool insert(const T& data) {}
};
但是这里还存在一个问题 – 我们在 insert 时需要比较 key 的大小来确定插入位置,在之前模拟实现的红黑树中,我们直接将节点定义为了pair
template <class T1, class T2>
bool operator< (const pair<T1,T2>& lhs, const pair<T1,T2>& rhs)
{
return lhs.first<rhs.first || (!(rhs.first<lhs.first) && lhs.second<rhs.second);
};
template <class T1, class T2>
bool operator> (const pair<T1,T2>& lhs, const pair<T1,T2>& rhs) { return rhs<lhs; }
所以我们也不能使用库中提供的默认比较函数,那么我们就只能自己写一个仿函数来完成红黑树节点数据 _data 的大小比较了;大家看上面的源码可以发现,stl 中也是这样来完成节点比较的 – 红黑树的第三个模板参数 _KeyOfValue:
//set.h
namespace thj {
template<class K>
class set {
public:
struct SetKeyOfT {
const K& operator()(const K& k) {
return k;
}
};
private:
RBTree<K, K, SetKeyOfT> _t;
};
}
//map.h
namespace thj {
template<class K, class V>
class map {
public:
struct MapKeyOfT {
const K& operator()(const pair<const K, V>& kv) {
return kv.first;
}
};
private:
RBTree<K, pair<const K, V>, MapKeyOfT> _t;
};
};
//RBTree.h
//map: RBTree, MapKeyOfT> _t;
//set: RBTree _t;
template<class K, class T, class KeyOfT>
class RBTree {
typedef RBTreeNode<T> Node;
public:
bool insert(const T& data) {
//判断根节点是否为空
if (_root == nullptr) {
_root = new Node(data);
_root->_col = BLACK; //根节点的颜色一定为黑
return true;
}
//寻找插入位置
KeyOfT kot; //仿函数对象
Node* parent = nullptr;
Node* cur = _root;
while (cur) {
if (kot(data) < kot(cur->_data)) {
parent = cur;
cur = cur->_left;
}
else if (kot(data) > .kot(cur->_data)) {
parent = cur;
cur = cur->_right;
}
else {
return false; //不允许重复节点
}
}
//走到空开始插入,注意这里还需要修改父节点的指向
//新增节点的颜色为默认被初始化为红色,所以这里不需要再显式赋值
Node* newNode = new Node(data);
if (kot(data) < kot(parent->_data))
parent->_left = newNode;
else
parent->_right = newNode;
newNode->_parent = parent; //修改父节点指向
//调整节点
}
private:
Node* _root;
};
红黑树的迭代器其实和 list 的迭代器差不多 – 单独为迭代器定义一个类,类中重载运算符来实现迭代器的各种行为,比如 operator*() 返回节点中的数据,operator->() 返回节点的地址,operator!=() 比较两个迭代器是否相等;其中,为了满足 const 迭代器返回 const T& 和 const T*,我们需要增加两个模板参数 Ref 和 Ptr;这些东西我们在模拟实现 list 的时候都已经详细讲过了,所以这里不再赘述。红黑树的迭代器和 list 迭代器不同的地方在于红黑树 end() 迭代器的位置以及迭代器如何进行 ++ 与 –;
end() 迭代器的位置
STL 明确规定,begin() 与 end() 代表的是一段前闭后开的区间,而对红黑树进行中序遍历后,可以得到一个有序的序列,因此,begin() 可以放在红黑树中最小节点 (即最左侧节点) 的位置,end() 可以放在最大节点 (最右侧节点) 的下一个位置;
为了让 end() 能够指向最右节点的下一个节点, stl 源码中增加了一个哨兵位的头结点,该节点的 left 指向这棵树的最左节点,也就是 begin(),right 指向这棵树的最右节点,parent 指向 nullptr,然后让根的父节点指向它,最右节点的 right 也指向它;所以在 stl 源码的实现中这个哨兵位的头结点就是 end():
但是我们前面模拟实现的红黑树并没有设置哨兵位头结点,而重新去改又太麻烦,所以我们下面就直接将 end() 定义为 nullptr 了:
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;
//begin是树的最左节点
iterator begin() {
if (_root == nullptr)
return iterator(nullptr);
Node* left = _root;
while (left->_left) {
left = left->_left;
}
return iterator(left);
}
//end定义为空
iterator end() {
return iterator(nullptr);
}
const_iterator begin() const {
if (_root == nullptr)
return const_iterator(nullptr);
Node* left = _root;
while (left->_left) {
left = left->_left;
}
return const_iterator(left);
}
const_iterator end() const {
return const_iterator(nullptr);
}
};
迭代器的 ++ 与 –
由于红黑树是一棵二叉搜索树,那么对红黑树进行中序遍历得到的是一个有序序列,所以红黑树的迭代器 ++ 就应该是跳到中序遍历中下一个节点的位置,-- 则是反过来跳到中序遍历中上一个节点的位置。
红黑树迭代器 ++ 一共分为两种情况:
如上,假设当前节点是13,可以看到13有右孩子,那么迭代器++就应该跳到13右孩子的最左节点,也就是15;如果当前节点是11,11没有右孩子,那么迭代器++就应该跳到当前节点为 父节点的左孩子 的那一个祖先节点,也就是13,为什么不能跳到8的位置呢?这是因为中序遍历的遍历顺序为 左 根 右,此时8已经被访问过了,所以我们需要找到 cur == parent->_left 的parent 节点,也就是 13;也只有这样,迭代器++才是按中序来走的。
红黑树迭代器 – 则是将 ++ 完全倒过来:
这里我就不再给出示例了,大家可以对照着上面的图自己走一走,看看–是不是按照中序遍历的倒序来走的。
迭代器代码如下:
//红黑树的迭代器
//__RBTreeIterator Self //普通迭代器
//__RBTreeIterator Self //const迭代器
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) const {
return _node != s._node;
}
bool operator==(const Self& s) const {
return _node == s._node;
}
//++迭代器
Self& operator++() {
//如果右子树不为空,则++迭代器为右子树的最左节点
if (_node->_right) {
Node* cur = _node->_right;
while (cur->_left)
cur = cur->_left;
_node = cur;
}
//如果右子树为空,则++迭代器为 cur为父节点的左孩子的那一个祖先节点
else {
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur != parent->_left) {
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
//迭代器++
Self& operator++(int) {
Self& tmp = *this;
operator++();
return tmp;
}
//--迭代器
Self& operator--() {
//如果左子树存在,则--迭代器为左子树的最右节点
if (_node->_left) {
Node* cur = _node->_left;
while (cur->_right)
cur = cur->_right;
_node = cur;
}
//如果左子树为空,则--迭代器为 cur为父节点的右孩子的那一个祖先节点
else {
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && parent->_right != cur) {
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
//迭代器--
Self& operator--(int) {
Self& tmp = *this;
operator--();
return tmp;
}
};
set 模拟实现的前面部分很简单,在类中定义一个 RBTree 类型的成员变量,然后插入、查找等函数都直接调用红黑树的对应函数即可;set 模拟实现的重难点在于 set 迭代器的实现;
我们可以直接将红黑树的迭代器封装为 set 的迭代器,如下:
//迭代器
//typename的作用是告诉编译器这是一个类型,而不是静态变量
typedef typename RBTree<K, K, SetKeyOfT>::iterator iterator;
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;
iterator begin() {
return _t.begin();
}
iterator end() {
return _t.end();
}
但是这样会存在一个问题 – 我们可以通过迭代器来修改 key 的值,很显然这是错误的,因为它可能会破坏搜索树的结构:
要解决这个问题也很简单,我们直接让 set 的普通迭代器和 const 迭代器都使用红黑树的 const 迭代器来进行封装即可,这样 set 迭代器解引用得到的就是 const K& 了;但是这样做之后发现虽然的确不能通过迭代器来修改 key 的值了,但是这里又发生了新的错误:
//使用红黑树的const迭代器来封装set的普通迭代器
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;
仔细观察思考其实可以发现报错是因为 _t.begin() 和 _t.end() 返回的都是红黑树的普通迭代器,而 set 中用于接受返回值的 iterator 其本质封装的是红黑树的 const 迭代器,所以这里报错的原因是 将红黑树的普通迭代器赋值给红黑树的 const 迭代器;
这个问题的解决办法有很多,比如 set 的 begin() 和 end() 调用红黑树的 cbegin() 和 cend(),这里我们参考 stl 库中的做法:
iterator begin() const { return _M_t.begin(); }
iterator end() const { return _M_t.end(); }
可以看到,stl 库中的解决办法很简单 – 将 begin() 和 end() 修饰为 const 成员函数即可;为什么这样简单的一步就可以呢?其实是因为当普通成员调用 const 成员函数时其权限会被缩小,也就是说,当 begin() 和 end() 变为 const 成员函数时 _t 就只能调用红黑树的 const 迭代器了,此时 iterator 就能够正确接受函数的返回值了;
iterator begin() const {
return _t.begin();
}
iterator end() const {
return _t.end();
}
set 初步模拟实现的代码如下 – 后面还会进行改动:
#pragma once
#include
#include "RBTree.h"
namespace thj {
template<class K>
class set {
public:
//仿函数
struct SetKeyOfT {
const K& operator()(const K& k) {
return k;
}
};
//迭代器
//typename的作用是告诉编译器这是一个类型,而不是静态变量
//使用红黑树的const迭代器来封装set的普通迭代器,从而保证K不能被修改
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();
}
//std::pair insert(const K& key) {
// //先用红黑树的普通迭代器接受插入函数的返回值
// std::pair::iterator, bool> p = _t.insert(key);
// //再用普通迭代器构造const迭代器进行返回--红黑树的const迭代器就是set的普通迭代器
// return std::pair< iterator, bool>(p.first, p.second);
//}
bool insert(const K& key) {
return _t.insert(key);
}
iterator find(const K& key) {
return _t.find(key);
}
private:
RBTree<K, K, SetKeyOfT> _t;
};
}
map 和 set 一样,模拟实现的前面部分很简单如插入、查找都直接复用红黑树的相应函数实现;不同的是,map 是 KV模型 的容器, KV模型 的 key 虽然也不允许修改,因为这可能会破坏搜索树的结构,但是 value 是运行修改的,所以 map 的普通迭代器封装红黑树的普通迭代器即可,而不用将其封装为红黑树的 const 迭代器;
同时,我们也不用担心 map 的 key 被修改的问题,因为 map 在定义红黑树的成员变量时传递给红黑树的 T 模板参数为 pair
typedef typename RBTree<K, std::pair<const K, V>, MapKeyOfT>::iterator iterator;
typedef typename RBTree<K, std::pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;
//const K保证了map的key不会被修改
RBTree<K, std::pair<const K, V>, MapKeyOfT> _t;
map 模拟实现的难点在于 operator[](const K& key) 函数的实现 – 在前面map 和 set 的使用那一节中我们知道 map 支持 [] 访问,并且 map 的 operator[] 其实同时具备插入、查找和修改 (修改 value) 的功能,下面我将之前博客中的图放出来便于大家回忆:。
现在我们的目标是要让自己模拟实现的 map 也支持 [] 操作,即也重载 operator[];很显然,要能够重载 [] 操作符我们首先要修改我们之前模拟实现中 insert 函数和 find 函数的返回值,并且要将 RBTree、map、set 三个地方都修改:
RBTree.h:
//返回值为 pair
std::pair<iterator, bool> insert(const T& data) {
//判断根节点是否为空
if (_root == nullptr) {
_root = new Node(data);
_root->_col = BLACK; //根节点的颜色一定为黑
return std::make_pair(iterator(_root), true);
}
//寻找插入位置
KeyOfT kot; //仿函数对象
Node* parent = nullptr;
Node* cur = _root;
while (cur) {
if (kot(data) < kot(cur->_data)) {
parent = cur;
cur = cur->_left;
}
else if (kot(data) > kot(cur->_data)) {
parent = cur;
cur = cur->_right;
}
else {
return std::make_pair(iterator(cur), false); //不允许重复节点
}
}
//走到空开始插入,注意这里还需要修改父节点的指向
//新增节点的颜色为默认被初始化为红色,所以这里不需要再显式赋值
Node* newNode = new Node(data);
if (kot(data) < kot(parent->_data))
parent->_left = newNode;
else
parent->_right = newNode;
newNode->_parent = parent; //修改父节点指向
//调整节点
return std::make_pair(iterator(newNode), true);
}
//查找
iterator find(const K& key) {
Node* cur = _root;
while (cur) {
if (key > cur->_key)
cur = cur->_right;
else if (key < cur->_key)
cur = cur->_left;
else
return iterator(cur);
}
//cur为空说明找不到
return end();
}
set.h:
std::pair<iterator, bool> insert(const K& key) {
return _t.insert(key);
}
iterator find(const K& key) {
return _t.find(key);
}
map.h:
std::pair<iterator, bool> insert(const std::pair<const K, V>& kv) {
return _t.insert(kv);
}
iterator find(const K& key) {
return _t.find(key);
}
V& operator[](const K& key) {
std::pair<iterator, bool> pos = insert(std::make_pair(key, V()));
return pos.first->second;
}
但是当我们修改完毕后运行程序我们会发现 set.h 中又有报错:
其实这次报错还是由红黑树的普通迭代器赋值给 const 迭代器造成的 – insert 函数返回的是由红黑树的普通迭代器以及布尔值构成的键值对,但是接受返回值的却是由红黑树的 const 迭代器 (set 的普通迭代器) 以及布尔值构成的键值对;
那么这次我们还能否像之前一样通过加 const 来解决呢?显然是不能的,这样并不起任何作用;那应该怎么做呢?还是参考源码:
//set的insert
std::pair<iterator, bool> insert(const value_type& __x)
{
std::pair<typename _Rep_type::iterator, bool> __p = _M_t._M_insert_unique(__x);
return std::pair<iterator, bool>(__p.first, __p.second);
}
//红黑树的迭代器
template <class value, class Ref, class Ptr>
struct _rb_tree_iterator : public _rb_tree_base_iterator
{
typedef _rb_tree_iterator<value, value&, value*> iterator;
typedef _rb_tree_iterator<value, Ref, Ptr> self;
typedef __rb_tree_node<value>* link_ type;
_rb_tree_iterator(const iterator& it) { node = it.node; } //拷贝构造
};
可以看到,stl 中并不是直接将 insert 函数的返回值进行返回,而是先将它的返回值赋值给一个键值对,该键值对由红黑树的普通迭代器和一个布尔值构成,然后再用该键值对构造一个由红黑树的 const 迭代器 (set 的普通迭代器) 和布尔值组成的键值对进行返回,这样就不会发生类型冲突了;
那么为什么红黑树的普通迭代器能够构造出 const 迭代器呢?答案在红黑树的迭代器中 – 可以看到红黑树的迭代器中貌似实现了一个拷贝构造函数,但奇怪的地方在于该函数的参数是一个普通迭代器,而不是 Self,而这就是关键所在:
所以我们可以通过红黑树迭代器类中的 构造函数 来实现用普通迭代器来构造 const 迭代器。
现在我们参照 stl 源码的思路来改造我们的迭代器和 set insert 函数:
__RBTreeIterator:
template<class T, class Ref, class Ptr>
struct __RBTreeIterator {
typedef RBTreeNode<T> Node;
typedef __RBTreeIterator<T, Ref, Ptr> Self;
typedef __RBTreeIterator<T, T&, T*> iterator; //普通迭代器
Node* _node;
__RBTreeIterator(Node* node)
: _node(node)
{}
//当模板实例化为时,iterator和Self相同,这是一个拷贝构造函数
//当模板实例化为时,Self是const迭代器,而iterator是普通迭代器,此时这是一个构造函数,它可以用普通迭代器构造一个const迭代器
__RBTreeIterator(const iterator& s)
: _node(s._node)
{}
};
set.h:
std::pair<iterator, bool> insert(const k& key) {
//先用红黑树的普通迭代器接受插入函数的返回值
std::pair<typename rbtree<k, k, setkeyoft>::iterator, bool> p = _t.insert(key);
//再用普通迭代器构造const迭代器进行返回--红黑树的const迭代器就是set的普通迭代器
return std::pair< iterator, bool>(p.first, p.second);
}
至此,我们使用红黑树来封装 set 和 map 的工作就终于完成了。
map 模拟实现的代码如下:
#pragma once
#include
#include "RBTree.h"
namespace thj {
template<class K, class V>
class map {
public:
//仿函数
struct MapKeyOfT {
const K& operator()(const std::pair<const K, V>& kv) {
return kv.first;
}
};
//迭代器
//typename的作用是告诉编译器这是一个类型,而不是静态变量
typedef typename RBTree<K, std::pair<const K, V>, MapKeyOfT>::iterator iterator;
typedef typename RBTree<K, std::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();
}
std::pair<iterator, bool> insert(const std::pair<const K, V>& kv) {
return _t.insert(kv);
}
iterator find(const K& key) {
return _t.find(key);
}
V& operator[](const K& key) {
std::pair<iterator, bool> pos = insert(std::make_pair(key, V()));
return pos.first->second;
}
private:
RBTree<K, std::pair<const K, V>, MapKeyOfT> _t;
};
};
#pragma once
#include
//节点颜色
enum Color {
RED,
BLACK
};
template<class T>
struct RBTreeNode {
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
T _data;
Color _col; //节点颜色
RBTreeNode(const T& data)
: _data(data)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _col(RED) //默认给成红色
{}
};
//红黑树的迭代器
template<class T, class Ref, class Ptr>
struct __RBTreeIterator {
typedef RBTreeNode<T> Node;
typedef __RBTreeIterator<T, Ref, Ptr> Self;
typedef __RBTreeIterator<T, T&, T*> iterator; //普通迭代器
Node* _node;
__RBTreeIterator(Node* node)
: _node(node)
{}
//当模板实例化为时,iterator和Self相同,这是一个拷贝构造函数
//当模板实例化为时,Self是const迭代器,而iterator是普通迭代器,
//此时这是一个构造函数,它可以用普通迭代器构造一个const迭代器
__RBTreeIterator(const iterator& s)
: _node(s._node)
{}
Ref operator*() {
return _node->_data;
}
Ptr operator->() {
return &(_node->_data);
}
bool operator!=(const Self& s) const {
return _node != s._node;
}
bool operator==(const Self& s) const {
return _node == s._node;
}
//++迭代器
Self& operator++() {
//如果右子树不为空,则++迭代器为右子树的最左节点
if (_node->_right) {
Node* cur = _node->_right;
while (cur->_left)
cur = cur->_left;
_node = cur;
}
//如果右子树为空,则++迭代器为 cur为父节点的左孩子的那一个祖先节点
else {
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur != parent->_left) {
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
//迭代器++
Self& operator++(int) {
Self& tmp = *this;
operator++();
return tmp;
}
//--迭代器
Self& operator--() {
//如果左子树存在,则--迭代器为左子树的最右节点
if (_node->_left) {
Node* cur = _node->_left;
while (cur->_right)
cur = cur->_right;
_node = cur;
}
//如果左子树为空,则--迭代器为 cur为父节点的右孩子的那一个祖先节点
else {
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && parent->_right != cur) {
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
//迭代器--
Self& operator--(int) {
Self& tmp = *this;
operator--();
return tmp;
}
};
//map: RBTree, MapKeyOfT> _t;
//set: RBTree _t;
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;
//begin是树的最左节点
iterator begin() {
if (_root == nullptr)
return iterator(nullptr);
Node* left = _root;
while (left->_left) {
left = left->_left;
}
return iterator(left);
}
//end是为空
iterator end() {
return iterator(nullptr);
}
const_iterator begin() const {
if (_root == nullptr)
return const_iterator(nullptr);
Node* left = _root;
while (left->_left) {
left = left->_left;
}
return const_iterator(left);
}
const_iterator end() const {
return const_iterator(nullptr);
}
//-----------------------------------------------------//
std::pair<iterator, bool> insert(const T& data) {
//判断根节点是否为空
if (_root == nullptr) {
_root = new Node(data);
_root->_col = BLACK; //根节点的颜色一定为黑
return std::make_pair(iterator(_root), true);
}
//寻找插入位置
KeyOfT kot; //仿函数对象
Node* parent = nullptr;
Node* cur = _root;
while (cur) {
if (kot(data) < kot(cur->_data)) {
parent = cur;
cur = cur->_left;
}
else if (kot(data) > kot(cur->_data)) {
parent = cur;
cur = cur->_right;
}
else {
return std::make_pair(iterator(cur), false); //不允许重复节点
}
}
//走到空开始插入,注意这里还需要修改父节点的指向
//新增节点的颜色为默认被初始化为红色,所以这里不需要再显式赋值
Node* newNode = new Node(data);
if (kot(data) < kot(parent->_data))
parent->_left = newNode;
else
parent->_right = newNode;
newNode->_parent = parent; //修改父节点指向
cur = newNode;
//如果父节点颜色为红色,则需要进行调整,且最多调整到根
while (parent && parent->_col == RED) {
Node* grandfater = parent->_parent; //祖父节点
if (grandfater == nullptr)
break;
//如果父节点在祖父节点的左边
if (parent == grandfater->_left) {
Node* uncle = grandfater->_right; //叔叔节点
//情况一:叔叔存在且为红--父和叔叔变黑,爷爷变红,继续向上调整
if (uncle && uncle->_col == RED) {
parent->_col = BLACK;
uncle->_col = BLACK;
grandfater->_col = RED;
//继续向上调整
cur = grandfater;
parent = cur->_parent;
}
//情况二:叔叔不存在或叔叔存在且为黑
else {
//如果cur在parent的左边,则右单旋--注意走到这里p已经是g的左边了
if (cur == parent->_left) {
rotateR(grandfater);
//更新节点颜色
parent->_col = BLACK;
cur->_col = grandfater->_col = RED;
}
else { //左右双旋
//先以parent为轴进行左单旋
rotateL(parent);
//再以grandfather进行右单旋
rotateR(grandfater);
//更新节点颜色--此时cur为子树的根节点
cur->_col = BLACK;
parent->_col = grandfater->_col = RED;
}
//最后需要跳出循环,因为此时不会再影响上层
break;
}
}
//如果父节点在祖父节点的右边
else {
Node* uncle = grandfater->_left; //叔叔节点
//情况一:叔叔存在且为红--父和叔叔变黑,爷爷变红,继续向上调整
if (uncle && uncle->_col == RED) {
parent->_col = BLACK;
uncle->_col = BLACK;
grandfater->_col = RED;
//继续向上调整
cur = grandfater;
parent = cur->_parent;
}
//情况二:叔叔不存在或叔叔存在且为黑
else {
//如果cur在parent的右边,则左单旋--注意走到这里p已经是g的右边了
if (cur == parent->_right) {
rotateL(grandfater);
//更新节点颜色
parent->_col = BLACK;
cur->_col = grandfater->_col = RED;
}
else { //右左双旋
//先以parent为轴进行右单旋
rotateR(parent);
//再以grandfather进行左单旋
rotateL(grandfater);
//更新节点颜色--此时cur为子树的根节点
cur->_col = BLACK;
parent->_col = grandfater->_col = RED;
}
//最后需要跳出循环,因为此时不会再影响上层
break;
}
}
}
//在循环外面将_root的颜色重新置黑,避免循环内部将根节点的颜色变红了
_root->_col = BLACK;
return std::make_pair(iterator(newNode), true);
}
//查找
iterator find(const K& key) {
Node* cur = _root;
while (cur) {
if (key > cur->_key)
cur = cur->_right;
else if (key < cur->_key)
cur = cur->_left;
else
return iterator(cur);
}
//cur为空说明找不到
return end();
}
//左单旋--右右
void rotateL(Node* parent) {
Node* subR = parent->_right; //右子树
Node* subRL = subR->_left; //右子树的左子树
//右子树的左子树链接到parent的右边
parent->_right = subRL;
if (subRL) //subR可能为空(h==0)
subRL->_parent = parent;
//parent链接到右子树的左边
subR->_left = parent;
Node* pparent = parent->_parent; //保存parent的父节点
parent->_parent = subR;
//让subR成为子树的根
if (pparent) {
if (pparent->_left == parent) {
pparent->_left = subR;
subR->_parent = pparent;
}
else {
pparent->_right = subR;
subR->_parent = pparent;
}
}
else { //如果parent的parent为空,说明parent是整棵树根节点,此时subR成为新的根节点
subR->_parent = nullptr;
_root = subR;
}
}
//右单旋--左左
void rotateR(Node* parent) {
Node* subL = parent->_left; //左子树
Node* subLR = subL->_right; //左子树的右子树
//将左子树的右子树链接到根的左边
parent->_left = subLR;
if (subLR) //应对h==0的情况
subLR->_parent = parent;
//将根链接到左子树的右边
subL->_right = parent;
Node* pparent = parent->_parent; //记录父节点的父节点
parent->_parent = subL;
//让subL成为子树的根
if (pparent) {
if (pparent->_left == parent) {
pparent->_left = subL;
subL->_parent = pparent;
}
else {
pparent->_right = subL;
subL->_parent = pparent;
}
}
else { //如果parent的parent为空,说明parent是整棵树的根节点,此时subL成为新的根节点
subL->_parent = nullptr;
_root = subL;
}
}
private:
Node* _root = nullptr;
};
#pragma once
#include
#include "RBTree.h"
namespace thj {
template<class K>
class set {
public:
//仿函数
struct SetKeyOfT {
const K& operator()(const K& k) {
return k;
}
};
//迭代器
//typename的作用是告诉编译器这是一个类型,而不是静态变量
//使用红黑树的const迭代器来封装set的普通迭代器,从而保证K不能被修改
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();
}
std::pair<iterator, bool> insert(const K& key) {
//先用红黑树的普通迭代器接受插入函数的返回值
std::pair<typename RBTree<K, K, SetKeyOfT>::iterator, bool> p = _t.insert(key);
//再用普通迭代器构造const迭代器进行返回--红黑树的const迭代器就是set的普通迭代器
return std::pair< iterator, bool>(p.first, p.second);
}
iterator find(const K& key) {
return _t.find(key);
}
private:
RBTree<K, K, SetKeyOfT> _t;
};
}
#pragma once
#include
#include "RBTree.h"
namespace thj {
template<class K, class V>
class map {
public:
//仿函数
struct MapKeyOfT {
const K& operator()(const std::pair<const K, V>& kv) {
return kv.first;
}
};
//迭代器
//typename的作用是告诉编译器这是一个类型,而不是静态变量
typedef typename RBTree<K, std::pair<const K, V>, MapKeyOfT>::iterator iterator;
typedef typename RBTree<K, std::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();
}
std::pair<iterator, bool> insert(const std::pair<const K, V>& kv) {
return _t.insert(kv);
}
iterator find(const K& key) {
return _t.find(key);
}
V& operator[](const K& key) {
std::pair<iterator, bool> pos = insert(std::make_pair(key, V()));
return pos.first->second;
}
private:
RBTree<K, std::pair<const K, V>, MapKeyOfT> _t;
};
};
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
using namespace std;
#include "RBTree.h"
#include "map.h"
#include "set.h"
#include
void set_test1() {
int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
thj::set<int> s;
for (auto e : a)
s.insert(e);
thj::set<int>::iterator it = s.begin();
while (it != s.end()) {
//*it = 10;
cout << *it << " ";
++it;
}
cout << endl;
}
void map_test1() {
int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
thj::map<int, int> m;
for (auto e : a)
m.insert(std::make_pair(e, e));
thj::map<int, int>::iterator it = m.begin();
while (it != m.end()) {
//it->first++;
it->second++;
cout << it->first << ":" << it->second << " ";
++it;
}
cout << endl;
}
void map_test2() {
string arr[] = { "苹果", "西瓜", "芒果", "西瓜", "苹果", "梨子", "西瓜","苹果", "香蕉", "西瓜", "香蕉" };
thj::map<string, int> countMap;
for (auto& str : arr)
countMap[str]++;
for (auto& kv : countMap)
cout << kv.first << ":" << kv.second << " ";
cout << endl;
}
void map_test3() {
srand((size_t)time(0));
thj::map<int, int> m;
int N = 1000;
for (int i = 0; i < N; i++) {
m.insert(make_pair(rand(), rand()));
}
auto it = m.begin();
while (it != m.end()) {
cout << it->first << endl;
++it;
}
}
int main() {
//set_test1();
//map_test1();
map_test2();
//map_test3();
return 0;
}