目录
一、map和set类模板
在同时封装 set
和 map
时,面临第一个问题:两者参数不匹配
set
只需要key
map
则需要key
和value
用红黑树同时封装出set和map时,set传给value的是一个value,map传给value的是一个pair,set和map传给红黑树的value决定了这棵树里面存的节点值类型。上层容器不同,底层红黑树的Key和T也不同。
参考库中的解决方案:管你是 k
还是 k/v
,我都看作 value_type
,获取 key
值时再另想其他方法解决
注:rb_tree 的参数3是获取 key
的方式(后续介绍),参数4是比较方式,参数5是空间配置器
能否省略 参数1 key_type
?
- 对于
set
来说,可以,因为冗余了 - 但对于
map
来说,不行,因为map
中的函数参数类型为key_type
,省略后就无法确定参数类型了,比如Find
、Erase
中都需要key_type
这个类型
在上层容器set中,K和T都代表Key,底层红黑树节点当中存储K和T都是一样的;map中,K代表键值Key,T代表由Key和Value构成的键值对,底层红黑树中只能存储T。所以红黑树为了满足同时支持set和map,节点当中存储T。
这就要对红黑树进行改动。
二、红黑树节点定义
1.红黑树的节点修改
template<class K,class V>
修改为
template<class T>
那么节点定义就修改为
template<class T>
struct RBTreeNode
{
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
T _data;//节点的值,_data里面存的是K就传K,存的是pair就传pair
Colour _col;
RBTreeNode(const T& data)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _data(data)
, _col(RED)
{}
};
2.仿函数
(1)节点比较大小时存在的问题
红黑树插入节点时,需要比较节点的大小,kv需要改成_data:
pair<iterator, bool> Insert(const T& data) { //... }
但是以上代码在插入新节和查找节点时,当和当前节点比较大小时,Key可以比较,但是pair比较不了,也就是set可以比较,但是map比较不了。这就需要写一个仿函数,如果是map就取_data里面的first也就是Key进行比较,通过泛型解决红黑树里面存的是什么。所以上层容器map需要向底层的红黑树提供仿函数来获取T里面的Key,这样无论上层容器是set还是map,都可以用统一的方式进行比较了。
Find函数也有相同问题
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (kot(cur->_data) > key)
cur = cur->_left;
else if (kot(cur->_data) < key)
cur = cur->_right;
else
return cur;
}
return nullptr;
}
可以看到,Find()
的参数类型为 K
此时面临着一个尴尬的问题:当 T
为 key
时,_data
不是 pair
,自然没有 first
和 second
,程序也就无法跑起来。
凡是涉及获取 key
的地方都有这个问题,因为此时的 _data
是不确定的,对于这种不确定的类型,一般使用 仿函数 解决。
(2)解决不同类型的 key 获取问题
现在可以看看库中 rb_tree
的参数3了,它是一个 函数对象,可以传递 仿函数,主要是用来从不同的 T
中获取 key
值
set
中的 key
值就是 key
,而 map
中的 key
值是 pair<K, V>
中的 first。
set有set的仿函数,map有map的仿函数,尽管set的仿函数看起来没有什么作用,但是,必须要把它传给底层红黑树,这样红黑树就能根据仿函数分别获取set的key和map的first。
①set的仿函数
template<class K>
class set
{
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
pair<iterator, bool> insert(const K& key)
{
return _t.Insert(key);
}
private:
RBTree<K, K, SetKeyOfT> _t;
};
②map的仿函数
template<class K, class V>
class map
{
struct MapKeyOfT
{
const K& operator()(const pair<const K, V>& kv)
{
return kv.first;
}
};
private:
RBTree<K, pair<const K, V>, MapKeyOfT> _t;
};
当我们得到不同的 key
值获取方式后,就可以更改 红黑树 中相应的代码了
比如:查找
Node* Find(const K& key)
{
Node* cur = _root;
KeyOfT kot;//创建一个对象,用来获取 key 值
while (cur)
{
if (kot(cur->_data) > key)
cur = cur->_left;
else if (kot(cur->_data) < key)
cur = cur->_right;
else
return cur;
}
return nullptr;
}
三、红黑树迭代器
map和set的迭代器的实现其实本质上是红黑树迭代器的实现,迭代器的实现需要定义模板类型、模板类型引用、模板类型指针。
将 红黑树 的节点再一次封装,构建一个单独的 迭代器 类
因为此时节点的模板参数有 K 和 V,所以 迭代器类 中也需要这两个参数
至于 迭代器类 设计时的精髓:不同类型的迭代器传递不同的参数 这里就不再展开叙述,简单来说,额外增加 Ref 和 Ptr 的目的是为了让 普通迭代器 和 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)
{}
//1、typedef __RBTreeIterator<T, T&, T*> iterator; // 拷贝构造
//
// //支持普通迭代器构造const迭代器的构造函数
//2、typedef __RBTreeIterator<T, const T&, const T*> const_iterator;
__RBTreeIterator(const __RBTreeIterator<T, T&, T*>& it)
:_node(it._node)
{}
private:
Node* _node;
};
其中的 Ref
、Ptr
具体是什么类型,取决于调用方传递了什么
1、移动操作
迭代器 最重要的操作莫过于 移动,红黑树 的迭代器是一个 双向迭代器,只支持 ++
和 --
操作
树形 结构的容器在进行遍历时,默认按 中序遍历 的顺序进行迭代器移动,因为这样遍历 二叉搜索树 后,结果为 有序
正向移动思路:
- 判断当前节点的右子树是否存在,如果存在,则移动至右子树中的最左节点
- 如果不存在,则移动至当前路径中 孩子节点为左孩子的父亲节点
- 如果父亲为空,则下一个节点就是空
-
Self& operator++() { if (_node->_right) { // 1、右不为空,下一个就是右子树的最左节点 Node* childLeft = _node->_right; while (childLeft->_left) { childLeft = childLeft->_left; } _node = childLeft; } else { // 2、右为空,沿着到根的路径,找孩子是父亲左的那个祖先 Node* cur = _node; Node* parent = cur->_parent; while (parent && cur == parent->_right) { cur = parent; parent = parent->_parent; } _node = parent; } return *this; }
为什么右子树不为空时,要访问 右子树的最左节点?
· 因为此时是正向移动,路径为左根右
,如果右边路径存在,就要从它的最左节点开始访问
为什么右子树为空时,要访问当前路径中 孩子节点为左孩子 的父亲节点?
· 因为 孩子节点为右孩子 的父亲节点已经被访问过了
-- 啥都反过来就行
Self& operator--()
{
if (_node->_left)
{
// 1、左不为空,下一个就是左子树的最左节点
Node* childRight = _node->_left;
while (childRight->_right)
{
childRight = childRight->_right;
}
_node = childRight;
}
else
{
// 2、左为空,沿着到根的路径,找孩子是父亲右的那个祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left)
{
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
2、解决 set 迭代器的非法操作
此时的代码仍然存在问题:set
中只有 key
,key
是不能修改的,但此时 set
中的 key
可以被修改!
void SetAndMapTest3()
{
vector<int> arr = { 8,6,3,2,1 };
set<int> s;
for (auto e : arr)
s.Insert(e);
cout << "修改前: ";
for (auto& e : s)
{
cout << e << " ";
e = 1;
}
cout << endl;
cout << "修改后: ";
for (auto& e : s)
cout << e << " ";
cout << endl;
}
此时居然能将 set
中的 key
进行修改!? 这是非常不合理的
库中给出的解决方案:对于 set
来说,无论是否为 const
迭代器,都使用 红黑树中的 const
迭代器进行适配
也就是说,锁死了 set
中迭代器的修改权限,此时自然无法修改 key
值
- 此时迭代器类中的
Ref
和Ptr
都是const
版本
set.h
//迭代器
typedef typename Tree::const_iterator iterator;
typedef typename Tree::const_iterator const_iterator;
typedef typename Tree::const_reverse_iterator reverse_iterator;
typedef typename Tree::const_reverse_iterator const_reverse_iterator;
修改完成,VS
启动,代码,运行
结果:出现了一个编译错误
注意: 先要把修改相关的代码屏蔽,否则会导致这个错误无法出现
出现错误的原因
- 在
set
中,普通对象调用begin()
或end()
时,返回的是 普通迭代器,但此时的iterator
是 const 迭代器,这就涉及一个类型转换问题了,其中的Ref
、Ptr
类型不匹配!
解决方案:在 红黑树迭代器类 中新增一个特殊的构造函数
- 当类模板实例化为 普通迭代器 时,就是一个普通的 拷贝构造 函数
- 当类模板实例化为
const
迭代器 时,则是一个特殊的构造函数 -> 将普通的迭代器对象 -> 构造为const
迭代器//1、typedef __RBTreeIterator<T, T&, T*> iterator; // 拷贝构造 // // //支持普通迭代器构造const迭代器的构造函数 //2、typedef __RBTreeIterator<T, const T&, const T*> const_iterator; __RBTreeIterator(const __RBTreeIterator<T, T&, T*>& it) :_node(it._node) {}
如何做到的?
-
当创建 set(普通对象) 中的普通迭代器时,因为此时是普通对象,所以 红黑树 底层会返回一个 普通迭代器,但对于 set 来说,无论是否为 const 对象,它要返回的都是 const 迭代器,于是它会把 红黑树返回的普通迭代器 -> 借助特殊的构造函数 -> 构造为 const 迭代器
- 如果
set
是const
对象,那么 红黑树 返回的就是const
迭代器,都不用进行类型转换了
这种写法对于 map
是否有影响?
- 没有影响,对于
map
来说,普通对象对应的就是普通迭代器,不存在 普通迭代器 转为const
迭代器 这种情况
新增这个特殊的构造函数后,能正常编译,将 e = 1
这条赋值语句取消注释,再编译,可以发现出现了预料中的报错信息:不能给常量对象赋值
注意: set
中的普通对象对应的也是 const
迭代器,但底层 红黑树 仍然是普通对象,返回的普通迭代器无法转换为 set
中的 const
迭代器,需要通过特殊构造函数解决;不能单纯的通过 const
修饰迭代器暴力解决问题,因为这样会出现 const const
的问题
四、RBTree完整代码
#pragma once
#include <iostream>
#include <assert.h>
#include <time.h>
using namespace std;
// 红黑树封装map、set所需的红黑树
enum Colour
{
RED,
BLACK,
};
template<class T>
struct RBTreeNode
{
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
T _data;//节点的值,_data里面存的是K就传K,存的是pair就传pair
Colour _col;
RBTreeNode(const T& data)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _data(data)
, _col(RED)
{}
};
template<class T, class Ref, class Ptr>
struct __RBTreeIterator
{
typedef RBTreeNode<T> Node;
typedef __RBTreeIterator<T, Ref, Ptr> Self;
// 节点指针构造迭代器
__RBTreeIterator(Node* node)
:_node(node)
{}
//1、typedef __RBTreeIterator<T, T&, T*> iterator; // 拷贝构造
//
// //支持普通迭代器构造const迭代器的构造函数
//2、typedef __RBTreeIterator<T, const T&, const T*> const_iterator;
__RBTreeIterator(const __RBTreeIterator<T, T&, T*>& it)
:_node(it._node)
{}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
bool operator!=(const Self& s)
{
return _node != s._node;
}
Self& operator++()
{
if (_node->_right)
{
// 1、右不为空,下一个就是右子树的最左节点
Node* childLeft = _node->_right;
while (childLeft->_left)
{
childLeft = childLeft->_left;
}
_node = childLeft;
}
else
{
// 2、右为空,沿着到根的路径,找孩子是父亲左的那个祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right)
{
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
Self& operator--()
{
if (_node->_left)
{
// 1、左不为空,下一个就是左子树的最左节点
Node* childRight = _node->_left;
while (childRight->_right)
{
childRight = childRight->_right;
}
_node = childRight;
}
else
{
// 2、左为空,沿着到根的路径,找孩子是父亲右的那个祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left)
{
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
private:
Node* _node;
};
// 仿函数
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 = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return iterator(cur);
}
iterator end()
{
return iterator(nullptr);
}
~RBTree()
{
_Destroy(_root);
_root = nullptr;
}
Node* Find(const K& key)
{
Node* cur = _root;
KeyOfT kot;//创建一个对象,用来获取 key 值
while (cur)
{
if (kot(cur->_data) > key)
cur = cur->_left;
else if (kot(cur->_data) < key)
cur = cur->_right;
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;
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (kot(cur->_data) > kot(data))// 往左找
{
parent = cur;
cur = cur->_left;
}
else if (kot(cur->_data) < kot(data))// 往右找
{
parent = cur;
cur = cur->_right;
}
else
{
return make_pair(iterator(cur), false); // 值相等
}
}
//2.走到这里,说明kv在树中不存在,需要插入data,并且cur已经为空,parent已经是叶子节点了
cur = new Node(data);
Node* newnode = cur;
if (parent->_data > data)
parent->_left = cur;
else
parent->_right = cur;
cur->_parent = parent; // 反向链接父亲
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if (grandfather->_left == parent)
{
Node* uncle = grandfather->_right;
// 情况1:uncle存在且为红,变色处理,并继续往上调整
if (uncle && uncle->_col == RED)
{
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
// 继续往上调整
cur = grandfather;
parent = cur->_parent;
}
else // 情况2+3:uncle不存在/uncle存在且为黑,旋转+变色
{
// g
// p u
// c
if (cur == parent->_left)
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
// g
// p u
// c
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
//parent->_col = RED;
grandfather->_col = RED;
}
break;
}
}
else // (grandfather->_right == parent)
{
// g
// u p
// c (c)
Node* uncle = grandfather->_left;
// 情况1:uncle存在且为红,变色处理,并继续往上调整
if (uncle && uncle->_col == RED)
{
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
// 继续往上调整
cur = grandfather;
parent = cur->_parent;
}
else // 情况2+3:uncle不存在/uncle存在且为黑,旋转+变色
{
// g
// u p
// c
if (cur == parent->_right)
{
RotateL(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
}
else
{
// g
// u p
// c
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return make_pair(iterator(newnode), true);
}
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);
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
int Height()
{
return _Height(_root);
}
private:
void _Destroy(Node* root)
{
if (root == nullptr)
return;
_Destroy(root->_left);
_Destroy(root->_right);
delete root;
}
bool _Check(Node* root, int blackNum, int 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);
}
int _Height(Node* root)
{
if (root == nullptr)
return 0;
int leftH = _Height(root->_left);
int rightH = _Height(root->_right);
return leftH > rightH ? leftH + 1 : rightH + 1;
}
void _InOrder(Node* root)
{
if (root == nullptr)
return;
_InOrder(root->_left);
cout << root->_kv.first << " ";
_InOrder(root->_right);
}
// 左单旋
void RotateL(Node* parent)
{
Node* childR = parent->_right;
Node* childRL = childR->_left;
parent->_right = childRL;
if (childRL)
childRL->_parent = parent; // 更新父亲
Node* ppnode = parent->_parent;
childR->_left = parent;
parent->_parent = childR; // 更新父亲
// 是一颗单独的树
if (ppnode == nullptr)
{
_root = childR;
_root->_parent = nullptr;
}
else
{
// 是一颗子树,判断是左子树还是右子树
if (ppnode->_left == parent)
{
ppnode->_left = childR;
}
else
{
ppnode->_right = childR;
}
childR->_parent = ppnode;// 更新父亲
}
}
// 右单旋
void RotateR(Node* parent)
{
Node* childL = parent->_left;
Node* childLR = childL->_right;
parent->_left = childLR;
if (childLR)
childLR->_parent = parent; // 更新父亲
Node* ppnode = parent->_parent;
childL->_right = parent;
parent->_parent = childL; // 更新父亲
// 是一颗单独的树
if (parent == _root)
{
_root = childL;
_root->_parent = nullptr;
}
else
{
// 是一颗子树,判断是左子树还是右子树
if (ppnode->_left == parent)
{
ppnode->_left = childL;
}
else
{
ppnode->_right = childL;
}
childL->_parent = ppnode;// 更新父亲
}
}
Node* _root = nullptr;
};
五、map模拟实现
调用红黑树对应接口实现map,插入和查找函数返回值当中的节点指针改为迭代器,增加operator[ ]的重载:
#pragma once
#include "RBTree.h"
namespace yrj
{
template<class K, class V>
class map
{
struct MapKeyOfT
{
const K& operator()(const pair<const K, V>& kv)
{
return kv.first;
}
};
public:
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
iterator begin()
{
return _t.begin();
}
iterator end()
{
return _t.end();
}
V& operator[](const K& key)
{
pair<iterator, bool> ret = _t.Insert(make_pair(key, V()));
return ret.first->second;
}
pair<iterator, bool> insert(const pair<const K, V>& kv)
{
return _t.Insert(kv);
}
private:
RBTree<K, pair<const K, V>, MapKeyOfT> _t;
};
void test_map1()
{
map<string, string> dict;
dict.insert(make_pair("sort", "排序"));
dict.insert(make_pair("string", "字符串"));
dict.insert(make_pair("count", "计数"));
dict.insert(make_pair("stirng", "(字符串)"));
map<string, string>::iterator it = dict.begin();
while (it != dict.end())
{
cout << it->first << ":" << it->second << endl;
++it;
}
cout << endl;
for (auto& kv : dict)
{
cout << kv.first << ":" << kv.second << endl;
}
cout << endl;
}
void test_map2()
{
string arr[] = { "西瓜","西瓜","苹果","桃子","西瓜", "李子","李子" };
map<string, int> countMap;
for (auto& e : arr)
{
countMap[e]++;
}
for (auto& kv : countMap)
{
cout << kv.first << ":" << kv.second << endl;
}
cout << endl;
}
}
六、set模拟实现
调用红黑树对应接口实现set,插入和查找函数返回值当中的节点指针改为迭代器:
#pragma once
#include "RBTree.h"
namespace yrj
{
template<class K>
class set
{
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
typedef typename RBTree<K, K, SetKeyOfT>::iterator iterator;
iterator begin()
{
return _t.begin();
}
iterator end()
{
return _t.end();
}
public:
pair<iterator, bool> insert(const K& key)
{
return _t.Insert(key);
}
private:
RBTree<K, K, SetKeyOfT> _t;
};
void test_set1()
{
set<int> s;
int a[] = { 4,25,6,1,3,5,15,7,16,14 };
for (auto e : a)
{
s.insert(e);
}
set<int>::iterator it = s.begin();
cout << "修改前: ";
while (it != s.end())
{
cout << *it << " ";
++it;
}
cout << endl;
cout << "修改后: ";
for (auto& e : s)
{
//e = 1;
cout << e << " ";
}
cout << endl;
}
}