目录
1.3.4 IsBalanceTree() 判断是否为平衡搜索树
一、红黑树(RBTree)
1.1 红黑树概念与性质
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。
性质:
1. 每个结点不是红色就是黑色
2. 根节点是黑色的
3. 如果一个节点是红色的,则它的两个孩子结点是黑色的
4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
如何理解红黑树最长路径长度不会超过最短路径的两倍?
结合性质4,最长路径呈一红一黑的样式向下,而最短路径是全黑向下!比如我们现在每条路径黑色节点树是3,那么最坏的那一条路径是黑 红 黑 红 黑 最短的是黑 黑 黑 前者要遍历5个节点,后者要遍历3个节点,通过数学分析我们也能得到这个结果,这里就不证明了~
1.2 红黑树节点的定义
//红黑树结点颜色
enum colour
{
RED,
BLACK
};
template <class ValType>
//结点类
struct TreeNode
{
public:
TreeNode(const ValType& val)
:_val(val)
,_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_colour(RED)
{}
ValType _val;
TreeNode<ValType>* _left;
TreeNode<ValType>* _right;
TreeNode<ValType>* _parent;
colour _colour;
};
插入的节点颜色为什么初始为红色?
我们通过红黑树的规则,后面用旋转的方式来解决不满足规则的情况,性质3是红色节点的约束条件,我们插入黑色节点很难判断是否打破规则,而红色节点的连续出现会告诉我们规则被打破了,要调整树!
1.3 红黑树模拟实现
1.3.1 红黑树成员框架
template <class ValType>
class RBTree
{
public:
typedef TreeNode<ValType> Node;
RBTree()
:_root(nullptr)
{}
void Inorder()
{
_Inorder(_root);
}
private:
void _Inorder(const Node* root)
{
if (root == nullptr)
return;
_Inorder(root->_left);
//cout << (root->_val).second << ' ';
cout <<"[" << (root->_val).first << "," << (root->_val).second <<"]" << endl;
_Inorder(root->_right);
}
Node* _root;
};
1.3.2 红黑树调整情形
约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点
情况一:cur为红,p为红,g为黑,u存在且为红
解决方式:将p,u改为黑,g改为红,然后把g当成cur,继续向上调整。
情况二:cur为红,p为红,g为黑,u不存在/u存在且为黑
解决方式:
p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反,
p为g的右孩子,cur为p的右孩子,则进行左单旋转
p、g变色--p变黑,g变红
情况三:cur为红,p为红,g为黑,u不存在/u存在且为黑
解决方式:
p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;相反,
p为g的右孩子,cur为p的左孩子,则针对p做右单旋转
则转换成了情况2
1.3.3 insert() 插入结点
void RotateR(Node* parent)
{
Node* SubL = parent->_left;
Node* SubLR = SubL->_right;
parent->_left = SubLR;
if (SubLR) SubLR->_parent = parent;
Node* gparent = parent->_parent;
if (parent == _root)
{
_root = SubL;
SubL->_parent = nullptr;
}
else {
if (parent == gparent->_left)
gparent->_left = SubL;
else gparent->_right = SubL;
SubL->_parent = parent->_parent;
}
SubL->_right = parent;
parent->_parent = SubL;
}
void RotateL(Node* parent)
{
Node* SubR = parent->_right;
Node* SubRL = SubR->_left;
parent->_right = SubRL;
if (SubRL) SubRL->_parent = parent;
Node* gparent = parent->_parent;
if (parent == _root)
{
_root = SubR;
SubR->_parent = nullptr;
}
else {
if (parent == gparent->_left)
gparent->_left = SubR;
else gparent->_right = SubR;
SubR->_parent = gparent;
}
SubR->_left = parent;
parent->_parent = SubR;
}
bool insert(const ValType& val)
{
if (_root == nullptr)
{
_root = new Node(val);
_root->_colour = BLACK;
return make_pair(iterator(_root), true);
}
Node* cur = _root;
Node* parent = nullptr;
//查找插入点
while (cur)
{
if (cur->val < val)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_val > val)
{
parent = cur;
cur = cur->_left;
}
else return false;
}
//链接关系
cur = new Node(val);
Node* newnode = cur;
cur->_parent = parent;
if (cur->_val > parent->_val)
parent->_right = cur;
else parent->_left = cur;
//插入后判断是否满足红黑树要求
while (parent && parent->_colour == RED)
{
Node* gparent = parent->_parent;
Node* uncle = nullptr;
if (parent == gparent->_left)
uncle = gparent->_right;
else uncle = gparent->_left;
//情况一
if (uncle && uncle->_colour == RED)
{
parent->_colour = uncle->_colour = BLACK;
gparent->_colour = RED;
cur = gparent;
parent = cur->_parent;
}
//情况二 + 情况三
else {
if (uncle == gparent->_right)
{
//情况二
if (cur == parent->_left)
{
RotateR(gparent);
gparent->_colour = RED;
parent->_colour = BLACK;
}
//情况三
else {
RotateL(parent);
RotateR(gparent);
gparent->_colour = RED;
cur->_colour = BLACK;
}
}
else {
//情况二
if (cur == parent->_right)
{
RotateL(gparent);
gparent->_colour = RED;
parent->_colour = BLACK;
}
//情况三
else {
RotateR(parent);
RotateL(gparent);
gparent->_colour = RED;
cur->_colour = BLACK;
}
}
}
}
//插入过程可能改变根的颜色,根必须为黑!!!
_root->_colour = BLACK;
return true;
1.3.4 IsBalanceTree() 判断是否为平衡搜索树
bool check(Node* root,int BlackNodeNum,int ref)
{
//走到空,判断此时黑节点累计数量是否满足标准
if (root == nullptr)
{
if (BlackNodeNum != ref)
return false;
return true;
}
//连续出现两次红色结点,不满足规则
if (root->_colour == RED && root->_parent->_colour == RED)
return false;
//每次遇到黑节点 ++黑节点个数
if (root->_colour == BLACK)
{
BlackNodeNum++;
}
//递归左右子树
return check(root->_left, BlackNodeNum, ref) && check(root->_right, BlackNodeNum, ref);
}
bool IsBalanceTree()
{
if (_root == nullptr)
return true;
if (_root->_colour == RED)
{
return false;
}
Node* left = _root;
//计算一条路径的黑色结点个数,以它为标准审视每条路径
int ref = 0;
int BlackNodeNum = 0;
while (left)
{
if (left->_colour == BLACK)
++ref;
left = left->_left;
}
return check(_root, BlackNodeNum, ref);
}
二、关联式容器与键值对
2.1 关联式容器概念
在初阶阶段,我们已经接触过STL中的部分容器,比如:vector、list、deque、
forward_list(C++11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。那什么是关联式容器?它与序列式容器有什么区别?
关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高。
2.2 键值对
用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。比如:现在要建立一个英汉互译的字典,那该字典中必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应该单词,在词典中就可以找到与其对应的中文含义
//STL对于键值对的定义
template <class T1, class T2>
struct pair
{
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
pair() : first(T1()), second(T2())
{}
pair(const T1& a, const T2& b) : first(a), second(b)
{}
};
三、树形结构的关联式容器
根据应用场景的不桶,STL总共实现了两种不同结构的管理式容器:树型结构与哈希结构。树型结构的关联式容器主要有四种:map、set、multimap、multiset。这四种容器的共同点是:使用平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列。下面一依次介绍每一个容器。
3.1 set
3.1.1 set的介绍
1. set是按照一定次序存储元素的容器
2. 在set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。
3. 在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行排序。
4. set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对
子集进行直接迭代。
5. set在底层是用二叉搜索树(红黑树)实现的。
注意:
1. 与map/multimap不同,map/multimap中存储的是真正的键值对<key, value>,set中只放value,但在底层实际存放的是由<value, value>构成的键值对。
2. set中插入元素时,只需要插入value即可,不需要构造键值对。
3. set中的元素不可以重复(因此可以使用set进行去重)。
4. 使用set的迭代器遍历set中的元素,可以得到有序序列
5. set中的元素默认按照小于来比较
6. set中查找某个元素,时间复杂度为:log_2 n
7. set中的元素不允许修改
8. set中的底层使用二叉搜索树(红黑树)来实现。
3.1.2 set的函数使用
set函数操作汇总链接:set - C++ Reference (cplusplus.com)
#include <set>
void TestSet()
{
// 用数组array中的元素构造set
int array[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0, 1, 3, 5, 7, 9, 2, 4,
6, 8, 0 };
set<int> s(array, array + sizeof(array) / sizeof(array));
cout << s.size() << endl;
// 正向打印set中的元素,从打印结果中可以看出:set可去重
for (auto& e : s)
cout << e << " ";
cout << endl;
// 使用迭代器逆向打印set中的元素
for (auto it = s.rbegin(); it != s.rend(); ++it)
cout << *it << " ";
cout << endl;
// set中值为3的元素出现了几次
cout << s.count(3) << endl;
}
3.2 map
3.2.1 map的介绍
1. map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素。
2. 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的
内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型
value_type绑定在一起,为其取别名称为pair:typedef pair<const key, T> value_type;
3. 在内部,map中的元素总是按照键值key进行比较排序的。
4. map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序
对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。
5. map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。
6. map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))。
3.2.2 map的函数接口介绍与使用
map函数操作汇总:map - C++ Reference (cplusplus.com)
注意:在元素访问时,有一个与operator[]类似的操作at()(该函数不常用)函数,都是通过
key找到与key对应的value然后返回其引用,不同的是:当key不存在时,operator[]用默认
value与key构造键值对然后插入,返回该默认value,at()函数直接抛异常!
void TestMap()
{
map<string, string> m;
// 向map中插入元素的方式:
// 将键值对<"peach","桃子">插入map中,用pair直接来构造键值对
m.insert(pair<string, string>("peach", "桃子"));
// 将键值对<"peach","桃子">插入map中,用make_pair函数来构造键值对
m.insert(make_pair("banan", "香蕉"));
// 借用operator[]向map中插入元素
/*
operator[]的原理是:
用<key, T()>构造一个键值对,然后调用insert()函数将该键值对插入到map中
如果key已经存在,插入失败,insert函数返回该key所在位置的迭代器
如果key不存在,插入成功,insert函数返回新插入元素所在位置的迭代器
operator[]函数最后将insert返回值键值对中的value返回
*/
// 将<"apple", "">插入map中,插入成功,返回value的引用,将“苹果”赋值给该引用结果,
m["apple"] = "苹果";
// key不存在时抛异常
//m.at("waterme") = "水蜜桃";
cout << m.size() << endl;
// 用迭代器去遍历map中的元素,可以得到一个按照key排序的序列
for (auto& e : m)
cout << e.first << "--->" << e.second << endl;
cout << endl;
// map中的键值对key一定是唯一的,如果key存在将插入失败
auto ret = m.insert(make_pair("peach", "桃色"));
if (ret.second)
cout << "<peach, 桃色>不在map中, 已经插入" << endl;
else
cout << "键值为peach的元素已经存在:" << ret.first->first << "--->"
<< ret.first->second << " 插入失败" << endl;
// 删除key为"apple"的元素
m.erase("apple");
if (1 == m.count("apple"))
cout << "apple还在" << endl;
else
cout << "apple被吃了" << endl;
}
3.3.3 multiset multimap
multiset:
multiset与set的区别是,multiset中的元素可以重复,set中value是唯一的!
multimap:
multimap和map的唯一不同就是:map中的key是唯一的,而multimap中key是可以
重复的。multimap中没有重载operator[]操作
四、模拟实现set 和 map
4.1 底层红黑树
set与map底层都是红黑树,我们需要进一步给红黑树添加类模板参数和迭代器的实现!
4.1.1 红黑树类模板参数
红黑树模板参数:template <class T,class ValType,class GetVal>
分别代表的是Key值 存储数据类型 插入时搜索树比较的值
当我们使用map的时候,map的存储数据是键值对,我们插入数据需要比较的是Key值(也可以是val),不能比较键值对所以需要提供获取键值对其中一个值的接口!
4.1.2 红黑树迭代器实现
迭代器类成员就是结点的指针!
这里重点提一下如何实现迭代器的++的功能:通过迭代器++,我们可以中序遍历红黑树结点,获得升序数据!以上图举例算法实现::
首先我们迭代器从最左端结点开始,从1结点开始,也就是说begin的时候迭代器返回1结点指针。
++,先判断右边是否为空,如果右不为空,那么继续探索返回右孩子的Min结点,如果右节点左为空,则直接返回右孩子!
如果结点的右孩子为空,判断结点与其父节点的关系,如果其是父节点的右孩子,说明本棵树已经已经访问完全,需要向上寻找祖先,直到其是父节点的左孩子! 这里如何理解呢?我们中序遍历的顺序是 左 中 右,右为空说明左和中已经访问完全!
当我们的6结点左右访问完全,下一个结点是8,6为1的右,我们向上寻找祖先,通过迭代cur 与 parent ,当前cur为6,parent为1,让1变为cur,8变为parent,因为1是8的左,说明中还没访问,所以停止寻找,让指针指向8!
//红黑树迭代器
template <class ValType ,class ref,class ptr>
// val 引用 指针
struct RBTreeiterator
{
typedef TreeNode<ValType> Node;
typedef RBTreeiterator<ValType,ref,ptr> self;
typedef RBTreeiterator<ValType, ValType&, ValType*> iterator;
Node* pnode;
RBTreeiterator(Node* p)
:pnode(p)
{}
//普通迭代器传给const迭代器
RBTreeiterator(const iterator& it)
:pnode(it.pnode)
{}
ref operator*()
{
return pnode->_val;
}
ptr operator->()
{
return &pnode->_val;
}
self& operator++()
{
//右不为空,寻找右的最左端,左端为空则返回自己
if (pnode->_right)
{
Node* left = pnode->_right;
while (left->_left)
{
left = left->_left;
}
pnode = left;
}
//右为空,寻找祖先
else
{
Node* cur = pnode;
Node* parent = cur->_parent;
//孩子是父节点的右孩子,迭代cur 和 parent
while (parent && cur == parent->_right)
{
cur = parent;
parent = cur->_parent;
}
//访问中
pnode = parent;
}
return *this;
}
bool operator!=(const self& s)
{
return pnode != s.pnode;
}
bool operator ==(const self& s)
{
return pnode == s.pnode;
}
红黑树迭代器接口:
typedef RBTreeiterator<ValType, ValType&, ValType*> iterator;
typedef RBTreeiterator<ValType, const ValType, const ValType*> const_iterator;
iterator begin()
{
Node* left = _root;
while (left && left->_left)
{
left = left->_left;
}
return iterator(left);
}
iterator end()
{
return iterator(nullptr);
}
//const迭代器
const_iterator begin()const
{
Node* left = _root;
while (left && left->_left)
{
left = left->_left;
}
return const_iterator(left);
}
const_iterator end()const
{
return const_iterator(nullptr);
}
4.1.3 红黑树完整版
//红黑树结点颜色
enum colour
{
RED,
BLACK
};
template <class ValType>
//结点类
struct TreeNode
{
public:
TreeNode(const ValType& val)
:_val(val)
,_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_colour(RED)
{}
ValType _val;
TreeNode<ValType>* _left;
TreeNode<ValType>* _right;
TreeNode<ValType>* _parent;
colour _colour;
};
//红黑树迭代器
template <class ValType ,class ref,class ptr>
struct RBTreeiterator
{
typedef TreeNode<ValType> Node;
typedef RBTreeiterator<ValType,ref,ptr> self;
typedef RBTreeiterator<ValType, ValType&, ValType*> iterator;
Node* pnode;
RBTreeiterator(Node* p)
:pnode(p)
{}
//普通迭代器传给const迭代器
RBTreeiterator(const iterator& it)
:pnode(it.pnode)
{}
ref operator*()
{
return pnode->_val;
}
ptr operator->()
{
return &pnode->_val;
}
self& operator++()
{
//右不为空,寻找右的最左端,左端为空则返回自己
if (pnode->_right)
{
Node* left = pnode->_right;
while (left->_left)
{
left = left->_left;
}
pnode = left;
}
//右为空,寻找祖先
else
{
Node* cur = pnode;
Node* parent = cur->_parent;
//孩子是父节点的右孩子,迭代cur 和 parent
while (parent && cur == parent->_right)
{
cur = parent;
parent = cur->_parent;
}
//访问中
pnode = parent;
}
return *this;
}
bool operator!=(const self& s)
{
return pnode != s.pnode;
}
bool operator ==(const self& s)
{
return pnode == s.pnode;
}
};
//红黑树
template <class T,class ValType,class GetVal>
class RBTree
{
public:
typedef TreeNode<ValType> Node;
typedef RBTreeiterator<ValType, ValType&, ValType*> iterator;
typedef RBTreeiterator<ValType, const ValType, const ValType*> const_iterator;
RBTree()
:_root(nullptr)
{}
void RotateR(Node* parent)
{
Node* SubL = parent->_left;
Node* SubLR = SubL->_right;
parent->_left = SubLR;
if (SubLR) SubLR->_parent = parent;
Node* gparent = parent->_parent;
if (parent == _root)
{
_root = SubL;
SubL->_parent = nullptr;
}
else {
if (parent == gparent->_left)
gparent->_left = SubL;
else gparent->_right = SubL;
SubL->_parent = parent->_parent;
}
SubL->_right = parent;
parent->_parent = SubL;
}
void RotateL(Node* parent)
{
Node* SubR = parent->_right;
Node* SubRL = SubR->_left;
parent->_right = SubRL;
if (SubRL) SubRL->_parent = parent;
Node* gparent = parent->_parent;
if (parent == _root)
{
_root = SubR;
SubR->_parent = nullptr;
}
else {
if (parent == gparent->_left)
gparent->_left = SubR;
else gparent->_right = SubR;
SubR->_parent = gparent;
}
SubR->_left = parent;
parent->_parent = SubR;
}
void Inorder()
{
_Inorder(_root);
}
pair<iterator,bool> insert(const ValType& val)
{
GetVal getval;
if (_root == nullptr)
{
_root = new Node(val);
_root->_colour = BLACK;
return make_pair(iterator(_root), true);
}
Node* cur = _root;
Node* parent = nullptr;
//查找插入点
while (cur)
{
if (getval(cur->_val) < getval(val))
{
parent = cur;
cur = cur->_right;
}
else if (getval(cur->_val) > getval(val))
{
parent = cur;
cur = cur->_left;
}
else return make_pair(iterator(cur), false);
}
//链接关系
cur = new Node(val);
Node* newnode = cur;
cur->_parent = parent;
if (cur->_val > parent->_val)
parent->_right = cur;
else parent->_left = cur;
//插入后判断是否满足红黑树要求
while (parent && parent->_colour == RED)
{
Node* gparent = parent->_parent;
Node* uncle = nullptr;
if (parent == gparent->_left)
uncle = gparent->_right;
else uncle = gparent->_left;
//情况一
if (uncle && uncle->_colour == RED)
{
parent->_colour = uncle->_colour = BLACK;
gparent->_colour = RED;
cur = gparent;
parent = cur->_parent;
}
//情况二 + 情况三
else {
if (uncle == gparent->_right)
{
//情况二
if (cur == parent->_left)
{
RotateR(gparent);
gparent->_colour = RED;
parent->_colour = BLACK;
}
//情况三
else {
RotateL(parent);
RotateR(gparent);
gparent->_colour = RED;
cur->_colour = BLACK;
}
}
else {
//情况二
if (cur == parent->_right)
{
RotateL(gparent);
gparent->_colour = RED;
parent->_colour = BLACK;
}
//情况三
else {
RotateR(parent);
RotateL(gparent);
gparent->_colour = RED;
cur->_colour = BLACK;
}
}
}
}
//插入过程可能改变根的颜色,根必须为黑!!!
_root->_colour = BLACK;
return make_pair(iterator(newnode), true);
}
bool check(Node* root,int BlackNodeNum,int ref)
{
//走到空,判断此时黑节点累计数量是否满足标准
if (root == nullptr)
{
if (BlackNodeNum != ref)
return false;
return true;
}
//连续出现两次红色结点,不满足规则
if (root->_colour == RED && root->_parent->_colour == RED)
return false;
//每次遇到黑节点 ++黑节点个数
if (root->_colour == BLACK)
{
BlackNodeNum++;
}
//递归左右子树
return check(root->_left, BlackNodeNum, ref) && check(root->_right, BlackNodeNum, ref);
}
bool IsBalanceTree()
{
if (_root == nullptr)
return true;
if (_root->_colour == RED)
{
return false;
}
Node* left = _root;
//计算一条路径的黑色结点个数,以它为标准审视每条路径
int ref = 0;
int BlackNodeNum = 0;
while (left)
{
if (left->_colour == BLACK)
++ref;
left = left->_left;
}
return check(_root, BlackNodeNum, ref);
}
iterator begin()
{
Node* left = _root;
while (left && left->_left)
{
left = left->_left;
}
return iterator(left);
}
iterator end()
{
return iterator(nullptr);
}
//const迭代器
const_iterator begin()const
{
Node* left = _root;
while (left && left->_left)
{
left = left->_left;
}
return const_iterator(left);
}
const_iterator end()const
{
return const_iterator(nullptr);
}
private:
void _Inorder(const Node* root)
{
if (root == nullptr)
return;
_Inorder(root->_left);
//cout << (root->_val).second << ' ';
cout <<"[" << (root->_val).first << "," << (root->_val).second <<"]" << endl;
_Inorder(root->_right);
}
Node* _root;
};
4.2 set的模拟实现
上面提到set的数据具有唯一性,所以set的迭代器也是const 迭代器,我们不能通过* 来修改set的数据!
set的接口都是调用红黑树的接口,迭代器是红黑树的迭代器!
namespace wyz
{
template <class K>
class set
{
public:
struct setGetVal
{
const K& operator()(const K& k)
{
return k;
}
};
typedef RBTree<K, K, setGetVal> RBTree;
typedef typename RBTree::const_iterator iterator;
typedef typename RBTree::const_iterator const_iterator;
pair<iterator, bool> insert(const K& k)
{
//注意ret类型中的迭代器是普通迭代器
pair<typename RBTree::iterator, bool> ret = _t.insert(k);
//我们这里需要用到普通迭代器拷贝构造const迭代器!!!
return make_pair(iterator(ret.first), ret.second);
}
void Inorder()
{
_t.Inorder();
}
bool IsBalanceTree()
{
return _t.IsBalanceTree();
}
iterator begin()const
{
return _t.begin();
}
iterator end()const
{
return _t.end();
}
private:
RBTree _t; //底层是红黑树
};
}
4.3 模拟实现map
map特别之处在于它的 [] 重载 我们可以通过 对象.[key] 访问val!
namespace wyz
{
template <class K, class V>
class map
{
public:
//我们通过key值比较,决定插入的位置
struct mapGetVal
{
const K& operator()(const pair<K, V>& kv)
{
return kv.first;
}
};
typedef RBTree<K, pair<K, V>, mapGetVal> RBTree;
typedef typename RBTree::iterator iterator;
typedef typename RBTree::const_iterator const_iterator;
pair<iterator, bool> insert(const pair<K, V>& kv)
{
return _t.insert(kv);
}
V& operator[](const K& key)
{
//这里绝妙就在构造匿名对象,如果没有对应的val,则插入V的匿名对象
pair<iterator, bool> ret=insert(make_pair(key, V()));
//返回val
return ret.first->second;
}
//中序遍历
void Inorder()
{
_t.Inorder();
}
//判断是否为红黑树
bool IsBalanceTree()
{
return _t.IsBalanceTree();
}
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 _t;
};
}
4.4 测试set和map
4.4.1 set 测试
void Test_set()
{
int arr[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
wyz::set<int> t;
for (auto e : arr)
{
t.insert(e);
}
wyz::set<int>::iterator it = t.begin();
while (it != t.end())
{
//(*it) += 10;
cout << *it << endl;
++it;
}
}
不允许解引用修改val !
4.4.2 map测试
void Test_map2()
{
string arr[] = { "苹果", "西瓜", "香蕉", "草莓", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
wyz::map<string, int> countMap;
for (auto e : arr)
{
countMap[e]++;
}
wyz::map<string, int>::const_iterator it = countMap.begin();
while (it != countMap.end())
{
cout <<it->first<<":"<< it->second << endl;
++it;
}
}