主页:醋溜马桶圈-CSDN博客
目录
4.2.5.2.1 情况一: cur为红,p为红,g为黑,u存在且为红
4.2.5.2.2 情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑
4.2.5.2.3 情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑
4.3.1.2 operator++()与operator--()
1. 关联式容器
在初阶阶段,我们已经接触过STL中的部分容器,比如:vector、list、deque、forward_list(C++11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。那什么是关联式容器?它与序列式容器有什么区别?
关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高
2. 键值对
用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。比如:现在要建立一个英汉互译的字典,那该字典中必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应该单词,在词典中就可以找到与其对应的中文含义
SGI-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)
{}
};
3. 树形结构的关联式容器
根据应用场景的不桶,STL总共实现了两种不同结构的管理式容器:树型结构与哈希结构。树型结构的关联式容器主要有四种:map、set、multimap、multiset。这四种容器的共同点是:使用平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列。下面一依次介绍每一个容器
3.1 set
3.1.1 set的介绍
cplusplus.com/reference/set/set/?kw=set
翻译:
- set是按照一定次序存储元素的容器
- 在set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除
- 在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行排序
- set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对子集进行直接迭代
- set在底层是用二叉搜索树(红黑树)实现的
注意:
- 与map/multimap不同,map/multimap中存储的是真正的键值对<key, value>,set中只放value,但在底层实际存放的是由<value, value>构成的键值对
- set中插入元素时,只需要插入value即可,不需要构造键值对
- set中的元素不可以重复(因此可以使用set进行去重)
- 使用set的迭代器遍历set中的元素,可以得到有序序列
- set中的元素默认按照小于来比较
- set中查找某个元素,时间复杂度为:log_2 n
- set中的元素不允许修改
- set中的底层使用二叉搜索树(红黑树)来实现
3.1.2 set的使用
3.1.2.1 set的模板参数列表
- T: set中存放元素的类型,实际在底层存储<value, value>的键值对
- Compare:set中元素默认按照小于来比较
- Alloc:set中元素空间的管理方式,使用STL提供的空间配置器管理
3.1.2.2 set的构造
3.1.2.3 set的迭代器
3.1.2.4 set的容量
3.1.2.5 set修改操作
3.1.2.6 set的使用举例
#include<iostream>
#include<set>
using namespace std;
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[0]));
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;
}
int main()
{
TestSet();
return 0;
}
3.2 map
3.2.1 map的介绍
cplusplus.com/reference/map/map/?kw=map
翻译:
- map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素
- 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起,为其取别名称为pair:
typedef pair<const key, T> value_type;- 在内部,map中的元素总是按照键值key进行比较排序的
- map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)
- map支持下标访问符,即在[]中放入key,就可以找到与key对应的value
- map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))
3.2.2 map的使用
3.2.2.1 map的模板参数说明
- key: 键值对中key的类型
- T: 键值对中value的类型
- Compare: 比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户 自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递)
- Alloc:通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的空间配置器
- 注意:在使用map时,需要包含头文件
3.2.2.3 map的迭代器
问题:当key不在map中时,通过operator获取对应value时会发生什么问题?
注意:在元素访问时,有一个与operator[]类似的操作at()(该函数不常用)函数,都是通过key找到与key对应的value然后返回其引用,不同的是:当key不存在时,operator[ ]用默认value与key构造键值对然后插入,返回该默认value,at()函数直接抛异常
3.2.2.5 map中元素的修改
#include<iostream>
#include <string>
#include <map>
using namespace std;
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;
}
int main()
{
TestMap();
return 0;
}
【总结】
- map中的的元素是键值对
- map中的key是唯一的,并且不能修改
- 默认按照小于的方式对key进行比较
- map中的元素如果用迭代器去遍历,可以得到一个有序的序列
- map的底层为平衡搜索树(红黑树),查找效率比较高O(log_2 N)
- 支持[]操作符,operator[]中实际进行插入查找
3.3 multiset
3.3.1 multiset的介绍
cplusplus.com/reference/set/multiset/?kw=multiset
[翻译]:
- multiset是按照特定顺序存储元素的容器,其中元素是可以重复的
- 在multiset中,元素的value也会识别它(因为multiset中本身存储的就是<value, value>组成的键值对,因此value本身就是key,key就是value,类型为T). multiset元素的值不能在容器中进行修改(因为元素总是const的),但可以从容器中插入或删除
- 在内部,multiset中的元素总是按照其内部比较规则(类型比较)所指示的特定严格弱排序准则进行排序
- multiset容器通过key访问单个元素的速度通常比unordered_multiset容器慢,但当使用迭代器遍历时会得到一个有序序列
- multiset底层结构为二叉搜索树(红黑树)
注意:
- multiset中再底层中存储的是<value, value>的键值对
- mtltiset的插入接口中只需要插入即可
- 与set的区别是,multiset中的元素可以重复,set是中value是唯一的
- 使用迭代器对multiset中的元素进行遍历,可以得到有序的序列
- multiset中的元素不能修改
- 在multiset中找某个元素,时间复杂度为O(log_2 N)
- multiset的作用:可以对元素进行排序
3.3.2 multiset的使用
此处只简单演示set与multiset的不同,其他接口接口与set相同
#include <set>
#include<iostream>
using namespace std;
int main()
{
int array[] = { 2, 1, 3, 9, 6, 0, 5, 8, 4, 7 };
// 注意:multiset在底层实际存储的是<int, int>的键值对
multiset<int> s(array, array + sizeof(array) / sizeof(array[0]));
for (auto& e : s)
cout << e << " ";
cout << endl;
return 0;
}
3.4 multimap
3.4.1 multimap的介绍
翻译:
- Multimaps是关联式容器,它按照特定的顺序,存储由key和value映射成的键值对<key,value>,其中多个键值对之间的key是可以重复的
- 在multimap中,通常按照key排序和惟一地标识元素,而映射的value存储与key关联的内容。key和value的类型可能不同,通过multimap内部的成员类型value_type组合在一起,
value_type是组合key和value的键值对:
typedef pair<const Key, T> value_type;- 在内部,multimap中的元素总是通过其内部比较对象,按照指定的特定严格弱排序标准对key进行排序的
- multimap通过key访问单个元素的速度通常比unordered_multimap容器慢,但是使用迭代器直接遍历multimap中的元素可以得到关于key有序的序列
- multimap在底层用二叉搜索树(红黑树)来实现
注意:multimap和map的唯一不同就是:map中的key是唯一的,而multimap中key是可以
重复的
3.4.2 multimap的使用
multimap中的接口可以参考map,功能都是类似的
注意:
- multimap中的key是可以重复的
- multimap中的元素默认将key按照小于来比较
- multimap中没有重载operator[ ]操作
- 使用时与map包含的头文件相同
4. 底层结构
前面对map/multimap/set/multiset进行了简单的介绍,在其文档介绍中发现,这几个容器有个共同点是:其底层都是按照二叉搜索树来实现的,但是二叉搜索树有其自身的缺陷,假如往树中插入的元素有序或者接近有序,二叉搜索树就会退化成单支树,时间复杂度会退化成O(N),因此map、set等关联式容器的底层结构是对二叉树进行了平衡处理,即采用平衡树来实现
4.1 AVL 树
4.1.1 AVL树的概念
二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度
一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:
- 它的左右子树都是AVL树
- 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)
如果一棵二叉搜索树是高度平衡的,它就是AVL树
如果它有n个结点,其高度可保持在O(log_2 n),搜索时间复杂度O($log_2 n$)
4.1.2 AVL树节点的定义
AVL树节点的定义:
template<class T>
struct AVLTreeNode
{
AVLTreeNode(const T& data)
: _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)
, _data(data), _bf(0)
{}
AVLTreeNode<T>* _pLeft; // 该节点的左孩子
AVLTreeNode<T>* _pRight; // 该节点的右孩子
AVLTreeNode<T>* _pParent; // 该节点的双亲
T _data;
int _bf; // 该节点的平衡因子
};
4.1.3 AVL树的插入
AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。那么AVL树的插入过程可以分为两步:
- 按照二叉搜索树的方式插入新节点
- 调整节点的平衡因子
bool Insert(const T& data)
{
// 1. 先按照二叉搜索树的规则将节点插入到AVL树中
// ...
// 2. 新节点插入后,AVL树的平衡性可能会遭到破坏,此时就需要更新平衡因子,
// 并检测是否破坏了AVL树的平衡性
/*
pCur插入后,pParent的平衡因子一定需要调整,在插入之前,pParent
的平衡因子分为三种情况:-1,0, 1, 分以下两种情况:
1. 如果pCur插入到pParent的左侧,只需给pParent的平衡因子-1即可
2. 如果pCur插入到pParent的右侧,只需给pParent的平衡因子+1即可
此时:pParent的平衡因子可能有三种情况:0,正负1, 正负2
1. 如果pParent的平衡因子为0,说明插入之前pParent的平衡因子为正负1,插入后被调整
成0,此时满足
AVL树的性质,插入成功
2. 如果pParent的平衡因子为正负1,说明插入前pParent的平衡因子一定为0,插入后被更
新成正负1,此
时以pParent为根的树的高度增加,需要继续向上更新
3. 如果pParent的平衡因子为正负2,则pParent的平衡因子违反平衡树的性质,需要对其进
行旋转处理
*/
while (pParent)
{
// 更新双亲的平衡因子
if (pCur == pParent->_pLeft)
pParent->_bf--;
else
pParent->_bf++;
// 更新后检测双亲的平衡因子
if (0 == pParent->_bf)
{
break;
}
else if (1 == pParent->_bf || -1 == pParent->_bf)
{
// 插入前双亲的平衡因子是0,插入后双亲的平衡因为为1 或者 -1 ,说明以双亲为根的二叉树
// 的高度增加了一层,因此需要继续向上调整
pCur = pParent;
pParent = pCur->_pParent;
}
else
{
// 双亲的平衡因子为正负2,违反了AVL树的平衡性,需要对以pParent
// 为根的树进行旋转处理
if (2 == pParent->_bf)
{
// ...
}
else
{
// ...
}
}
}
return true;
}
4.1.4 AVL树的旋转
如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构,使之平衡化。根据节点插入位置的不同,AVL树的旋转分为四种:
4.1.4.1 右单旋
新节点插入较高左子树的左侧---左左:右单旋
void _RotateR(PNode pParent)
{
// pSubL: pParent的左孩子
// pSubLR: pParent左孩子的右孩子,注意:该
PNode pSubL = pParent->_pLeft;
PNode pSubLR = pSubL->_pRight;
// 旋转完成之后,30的右孩子作为双亲的左孩子
pParent->_pLeft = pSubLR;
// 如果30的左孩子的右孩子存在,更新亲双亲
if (pSubLR)
pSubLR->_pParent = pParent;
// 60 作为 30的右孩子
pSubL->_pRight = pParent;
// 因为60可能是棵子树,因此在更新其双亲前必须先保存60的双亲
PNode pPParent = pParent->_pParent;
// 更新60的双亲
pParent->_pParent = pSubL;
// 更新30的双亲
pSubL->_pParent = pPParent;
// 如果60是根节点,根新指向根节点的指针
if (NULL == pPParent)
{
_pRoot = pSubL;
pSubL->_pParent = NULL;
}
else
{
// 如果60是子树,可能是其双亲的左子树,也可能是右子树
if (pPParent->_pLeft == pParent)
pPParent->_pLeft = pSubL;
else
pPParent->_pRight = pSubL;
}
// 根据调整后的结构更新部分节点的平衡因子
pParent->_bf = pSubL->_bf = 0;
}
4.1.4.2 左单旋
新节点插入较高右子树的右侧---右右:左单旋
实现及情况考虑可参考右单旋
4.1.4.3 先左单旋再右单旋
新节点插入较高左子树的右侧---左右:先左单旋再右单旋
将双旋变成单旋后再旋转,即:先对30进行左单旋,然后再对90进行右单旋,旋转完成后再考虑平衡因子的更新
// 旋转之前,60的平衡因子可能是-1/0/1,旋转完成之后,根据情况对其他节点的平衡因子进行调整
void _RotateLR(PNode pParent)
{
PNode pSubL = pParent->_pLeft;
PNode pSubLR = pSubL->_pRight;
// 旋转之前,保存pSubLR的平衡因子,旋转完成之后,需要根据该平衡因子来调整其他节点的平衡因子
int bf = pSubLR->_bf;
// 先对30进行左单旋
_RotateL(pParent->_pLeft);
// 再对90进行右单旋
_RotateR(pParent);
if (1 == bf)
pSubL->_bf = -1;
else if (-1 == bf)
pParent->_bf = 1;
}
4.1.4.4 先右单旋再左单旋
新节点插入较高右子树的左侧---右左:先右单旋再左单旋
参考右左双旋
总结:
假如以pParent为根的子树不平衡,即pParent的平衡因子为2或者-2,分以下情况考虑
1. pParent的平衡因子为2,说明pParent的右子树高,设pParent的右子树的根为pSubR
- 当pSubR的平衡因子为1时,执行左单旋
- 当pSubR的平衡因子为-1时,执行右左双旋
2. pParent的平衡因子为-2,说明pParent的左子树高,设pParent的左子树的根为pSubL
- 当pSubL的平衡因子为-1是,执行右单旋
- 当pSubL的平衡因子为1时,执行左右双旋
旋转完成后,原pParent为根的子树个高度降低,已经平衡,不需要再向上更新。
4.1.5 AVL树的验证
AVL树是在二叉搜索树的基础上加入了平衡性的限制,因此要验证AVL树,可以分两步:
4.1.5.1 验证其为二叉搜索树
如果中序遍历可得到一个有序的序列,就说明为二叉搜索树
4.1.5.2 验证其为平衡树
- 每个节点子树高度差的绝对值不超过1(注意节点中如果没有平衡因子)
- 节点的平衡因子是否计算正确
int _Height(PNode pRoot);
bool _IsBalanceTree(PNode pRoot)
{
// 空树也是AVL树
if (nullptr == pRoot) return true;
// 计算pRoot节点的平衡因子:即pRoot左右子树的高度差
int leftHeight = _Height(pRoot->_pLeft);
int rightHeight = _Height(pRoot->_pRight);
int diff = rightHeight - leftHeight;
// 如果计算出的平衡因子与pRoot的平衡因子不相等,或者
// pRoot平衡因子的绝对值超过1,则一定不是AVL树
if (diff != pRoot->_bf || (diff > 1 || diff < -1))
return false;
// pRoot的左和右如果都是AVL树,则该树一定是AVL树
return _IsBalanceTree(pRoot->_pLeft) && _IsBalanceTree(pRoot -> _pRight);
}
4.1.6 AVL树的删除(了解)
因为AVL树也是二叉搜索树,可按照二叉搜索树的方式将节点删除,然后再更新平衡因子,只不错与删除不同的时,删除节点后的平衡因子更新,最差情况下一直要调整到根节点的位置。
具体实现可参考《算法导论》或《数据结构-用面向对象方法与C++描述》殷人昆版。
4.1.7 AVL树的性能
AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即$log_2 (N)$。但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合
4.1.8 AVl树的模拟实现
AVLTree.h
#pragma once
#include<iostream>
#include<assert.h>
#include<vector>
using namespace std;
template<class K, class V>
struct AVLTreeNode
{
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
pair<K, V> _kv;
int _bf;//平衡因子
AVLTreeNode(const pair<K, V>& kv)
: _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _kv(kv)
, _bf(0)
{}
};
template<class K, class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(kv);
if (parent->_kv.first < kv.first)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
cur->_parent = parent;
//...
//更新平衡因子
while (parent)
{
if (cur == parent->_left)
{
parent->_bf--;
}
else
{
parent->_bf++;
}
if (parent->_bf == 0)
{
//更新结束
break;
}
else if (parent->_bf == 1 || parent->_bf == -1)
{
//继续往上更新
cur = parent;
parent = parent->_parent;
}
else if (parent->_bf == 2 || parent->_bf == -2)
{
//当前子树出问题,旋转平衡
if (parent->_bf == -2 && cur->_bf == -1)
{
RotateR(parent);
}
else if (parent->_bf == 2 && cur->_bf == 1)
{
RotateL(parent);
}
else if (parent->_bf == 2 && cur->_bf == -1)
{
RotateRL(parent);
}
else if (parent->_bf == -2 && cur->_bf == 1)
{
RotateLR(parent);
}
break;
}
else
{
assert(false);
}
}
return true;
}
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < key)
{
cur = cur->_right;
}
else if (cur->_kv.first > key)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
//右单旋
void RotateR(Node* parent)
{
Node* subL = parent->_left;//左孩子
Node* subLR = subL->_right;//左孩子的右孩子
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
subL->_right = parent;
Node* ppNode = parent->_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;
}
parent->_bf = subL->_bf = 0;
}
//左单旋
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
subR->_left = parent;
Node* ppNode = parent->_parent;
parent->_parent = subR;
if (parent == _root)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
if (ppNode->_right == parent)
{
ppNode->_right = subR;
}
else
{
ppNode->_left = subR;
}
subR->_parent = ppNode;
}
parent->_bf = subR->_bf = 0;
}
//右左双旋
void RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
RotateR(parent->_right);
RotateL(parent);
if (bf == 1)
{
subRL->_bf = 0;
subR->_bf = 0;
parent->_bf = -1;
}
else if (bf == -1)
{
subRL->_bf = 0;
subR->_bf = 1;
parent->_bf = 0;
}
else if (bf == 0)
{
subRL->_bf = 0;
subR->_bf = 0;
parent->_bf = 0;
}
else
{
assert(false);
}
}
//左右双旋
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
RotateL(parent->_left);
RotateR(parent);
if (bf == -1)
{
subLR->_bf = 0;
subL->_bf = 0;
parent->_bf = 1;
}
else if (bf == 1)
{
subLR->_bf = 0;
subL->_bf = -1;
parent->_bf = 0;
}
else if (bf == 0)
{
subLR->_bf = 0;
subL->_bf = 0;
parent->_bf = 0;
}
else
{
assert(false);
}
}
//判断平衡
bool IsBalance()
{
return _IsBalance(_root);
}
//高度
int Height()
{
return _Height(_root);
}
//
int Size()
{
return _Size(_root);
}
private:
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_InOrder(root->_right);
}
int _Size(Node* root)
{
return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;
}
int _Height(Node* root)
{
if (root == nullptr)
return 0;
//int leftHeight = _Height(root->_left);
//int rightHeight = _Height(root->_right);
return max(_Height(root->_left), _Height(root->_right)) + 1;
}
bool _IsBalance(Node* root)
{
if (root == nullptr)
return true;
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
// 不平衡
if (abs(leftHeight - rightHeight) >= 2)
{
cout << root->_kv.first << endl;
return false;
}
// 顺便检查一下平衡因子是否正确
if (rightHeight - leftHeight != root->_bf)
{
cout << root->_kv.first << endl;
return false;
}
return _IsBalance(root->_left)
&& _IsBalance(root->_right);
}
private:
Node* _root = nullptr;
};
4.2 红黑树
4.2.1 红黑树的概念
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的
4.2.2 红黑树的性质
- 每个结点不是红色就是黑色
- 根节点是黑色的
- 如果一个节点是红色的,则它的两个孩子结点是黑色的
- 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点
- 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
4.2.3 红黑树节点的定义
// 节点的颜色
enum Color { RED, BLACK };
// 红黑树节点的定义
template<class ValueType>
struct RBTreeNode
{
RBTreeNode(const ValueType& data = ValueType(),Color color = RED)
: _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)
, _data(data), _color(color)
{}
RBTreeNode<ValueType>* _pLeft; // 节点的左孩子
RBTreeNode<ValueType>* _pRight; // 节点的右孩子
RBTreeNode<ValueType>* _pParent; // 节点的双亲(红黑树需要旋转,为了实现简单给出该字段)
ValueType _data;// 节点的值域
Color _color; // 节点的颜色
};
4.2.4 红黑树结构
为了后续实现关联式容器简单,红黑树的实现中增加一个头结点,因为跟节点必须为黑色,为了与根节点进行区分,将头结点给成黑色,并且让头结点的 pParent 域指向红黑树的根节点,pLeft域指向红黑树中最小的节点,_pRight域指向红黑树中最大的节点,如下:
4.2.5 红黑树的插入操作
红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:
4.2.5.1 按照二叉搜索的树规则插入新节点
template<class ValueType>
class RBTree
{
//……
bool Insert(const ValueType& data)
{
PNode& pRoot = GetRoot();
if (nullptr == pRoot)
{
pRoot = new Node(data, BLACK);
// 根的双亲为头节点
pRoot->_pParent = _pHead;
_pHead->_pParent = pRoot;
}
else
{
// 1. 按照二叉搜索的树方式插入新节点
// 2. 检测新节点插入后,红黑树的性质是否造到破坏,
// 若满足直接退出,否则对红黑树进行旋转着色处理
}
// 根节点的颜色可能被修改,将其改回黑色
pRoot->_color = BLACK;
_pHead->_pLeft = LeftMost();
_pHead->_pRight = RightMost();
return true;
}
private:
PNode& GetRoot() { return _pHead->_pParent; }
// 获取红黑树中最小节点,即最左侧节点
PNode LeftMost();
// 获取红黑树中最大节点,即最右侧节点
PNode RightMost();
private:
PNode _pHead;
};
4.2.5.2 检测新节点插入后,红黑树的性质是否造到破坏
因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此时需要对红黑树分情况来讨论:
约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点
4.2.5.2.1 情况一: cur为红,p为红,g为黑,u存在且为红
cur和p均为红,违反了性质三,此处能否将p直接改为黑?
解决方式:将p,u改为黑,g改为红,然后把g当成cur,继续向上调整。
4.2.5.2.2 情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑
- p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反,
- p为g的右孩子,cur为p的右孩子,则进行左单旋转
- p、g变色--p变黑,g变红
4.2.5.2.3 情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑
- p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;相反,
- p为g的右孩子,cur为p的左孩子,则针对p做右单旋转
- 则转换成了情况2
针对每种情况进行相应的处理即可
bool Insert(const ValueType& data)
{
// ...
// 新节点插入后,如果其双亲节点的颜色为空色,则违反性质3:不能有连在一起的红色结点
while (pParent && RED == pParent->_color)
{
// 注意:grandFather一定存在
// 因为pParent存在,且不是黑色节点,则pParent一定不是根,则其一定有双亲
PNode grandFather = pParent->_pParent;
// 先讨论左侧情况
if (pParent == grandFather->_pLeft)
{
PNode unclue = grandFather->_pRight;
// 情况三:叔叔节点存在,且为红
if (unclue && RED == unclue->_color)
{
pParent->_color = BLACK;
unclue->_color = BLACK;
grandFather->_color = RED;
pCur = grandFather;
pParent = pCur->_pParent;
}
else
{
// 情况五:叔叔节点不存在,或者叔叔节点存在且为黑
if (pCur == pParent->_pRight)
{
_RotateLeft(pParent);
swap(pParent, pCur);
}
// 情况五最后转化成情况四
grandFather->_color = RED;
pParent->_color = BLACK;
_RotateRight(grandFather);
}
}
else
{
// ...
}
}
// ...
}
4.2.6 红黑树的验证
红黑树的检测分为两步:
- 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
- 检测其是否满足红黑树的性质
bool IsValidRBTree()
{
PNode pRoot = GetRoot();
// 空树也是红黑树
if (nullptr == pRoot)
return true;
// 检测根节点是否满足情况
if (BLACK != pRoot->_color)
{
cout << "违反红黑树性质二:根节点必须为黑色" << endl;
return false;
}
// 获取任意一条路径中黑色节点的个数
size_t blackCount = 0;
PNode pCur = pRoot;
while (pCur)
{
if (BLACK == pCur->_color)
blackCount++;
pCur = pCur->_pLeft;
}
// 检测是否满足红黑树的性质,k用来记录路径中黑色节点的个数
size_t k = 0;
return _IsValidRBTree(pRoot, k, blackCount);
}
bool _IsValidRBTree(PNode pRoot, size_t k, const size_t blackCount)
{
//走到null之后,判断k和black是否相等
if (nullptr == pRoot)
{
if (k != blackCount)
{
cout << "违反性质四:每条路径中黑色节点的个数必须相同" << endl;
return false;
}
return true;
}
// 统计黑色节点的个数
if (BLACK == pRoot->_color)
k++;
// 检测当前节点与其双亲是否都为红色
PNode pParent = pRoot->_pParent;
if (pParent && RED == pParent->_color && RED == pRoot->_color)
{
cout << "违反性质三:没有连在一起的红色节点" << endl;
return false;
}
return _IsValidRBTree(pRoot->_pLeft, k, blackCount) &&
_IsValidRBTree(pRoot->_pRight, k, blackCount);
}
4.2.7 红黑树的删除
红黑树的删除本节不做讲解,可参考:《算法导论》或者《STL源码剖析》
红黑树 - _Never_ - 博客园 (cnblogs.com)
4.2.8 红黑树与AVL树的比较
红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O($log_2 N$),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多
4.2.9 红黑树的应用
- C++ STL库 -- map/set、mutil_map/mutil_set
- Java 库
- linux内核
- 其他一些库
4.2.10 红黑树的模拟实现
RBTree.h
#pragma once
#include<vector>
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;
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:
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col = BLACK;
return true;
}
Node* parent = nullptr;//保存parent
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(kv);
cur->_col = RED; // 新增节点给红色
if (parent->_kv.first < kv.first)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
cur->_parent = parent;
//parent存在且为红色,向上变色
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
//如果uncle存在且为红色,变色即可
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
//继续向上处理
cur = grandfather;
parent = cur->_parent;
}
//叔叔不存在或者存在且为黑
else
{
if (cur == parent->_left)
{
// g
// p u
//c
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
// g
// p u
// c
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
else
{
Node* uncle = grandfather->_left;
// 叔叔存在且为红,变色即可
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
// 继续往上处理
cur = grandfather;
parent = cur->_parent;
}
// 叔叔不存在,或者存在且为黑
else
{
// 情况二:叔叔不存在或者存在且为黑
// 旋转+变色
// g
// u p
// c
if (cur == parent->_right)
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
// g
// u p
// c
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return true;
}
//右旋
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
subL->_right = parent;
Node* ppNode = parent->_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;
}
}
//左旋
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
subR->_left = parent;
Node* ppNode = parent->_parent;
parent->_parent = subR;
if (parent == _root)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
if (ppNode->_right == parent)
{
ppNode->_right = subR;
}
else
{
ppNode->_left = subR;
}
subR->_parent = ppNode;
}
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
bool IsBalance()
{
if (_root->_col == RED)
{
return false;
}
int refNum = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK)
{
++refNum;
}
cur = cur->_left;
}
return Check(_root, 0, refNum);
}
private:
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_InOrder(root->_right);
}
bool Check(Node* root, int blackNum, const int refNum)
{
if (root == nullptr)
{
//cout << blackNum << endl;
if (refNum != blackNum)
{
cout << "存在黑色节点的数量不相等的路径" << endl;
return false;
}
return true;
}
if (root->_col == RED && root->_parent->_col == RED)
{
cout << root->_kv.first << "存在连续的红色节点" << endl;
return false;
}
if (root->_col == BLACK)
{
blackNum++;
}
return Check(root->_left, blackNum, refNum)
&& Check(root->_right, blackNum, refNum);
}
private:
Node* _root = nullptr;
};
4.3 红黑树模拟实现STL中的map与set
4.3.1 红黑树的迭代器
迭代器的好处是可以方便遍历,是数据结构的底层实现与用户透明。如果想要给红黑树增加迭代器,需要考虑以前问题:
4.3.1.1 begin()与end()
STL明确规定,begin()与end()代表的是一段前闭后开的区间,而对红黑树进行中序遍历后,可以得到一个有序的序列,因此:begin()可以放在红黑树中最小节点(即最左侧节点)的位置,end()放在最大节点(最右侧节点)的下一个位置,关键是最大节点的下一个位置在哪块?能否给成nullptr呢?答案是行不通的,因为对end()位置的迭代器进行--操作,必须要能找最后一个元素,此处就不行,因此最好的方式是将end()放在头结点的位置:
4.3.1.2 operator++()与operator--()
// 找迭代器的下一个节点,下一个节点肯定比其大
void Increasement()
{
//分两种情况讨论:_pNode的右子树存在和不存在
// 右子树存在
if (_pNode->_pRight)
{
// 右子树中最小的节点,即右子树中最左侧节点
_pNode = _pNode->_pRight;
while (_pNode->_pLeft)
_pNode = _pNode->_pLeft;
}
else
{
// 右子树不存在,向上查找,直到_pNode != pParent->right
PNode pParent = _pNode->_pParent;
while (pParent->_pRight == _pNode)
{
_pNode = pParent;
pParent = _pNode->_pParent;
}
// 特殊情况:根节点没有右子树
if (_pNode->_pRight != pParent)
_pNode = pParent;
}
}
// 获取迭代器指向节点的前一个节点
void Decreasement()
{
//分三种情况讨论:_pNode 在head的位置,_pNode 左子树存在,_pNode 左子树不
存在
// 1. _pNode 在head的位置,--应该将_pNode放在红黑树中最大节点的位置
if (_pNode->_pParent->_pParent == _pNode && _pNode->_color == RED)
_pNode = _pNode->_pRight;
else if (_pNode->_pLeft)
{
// 2. _pNode的左子树存在,在左子树中找最大的节点,即左子树中最右侧节点
_pNode = _pNode->_pLeft;
while (_pNode->_pRight)
_pNode = _pNode->_pRight;
}
else
{
// _pNode的左子树不存在,只能向上找
PNode pParent = _pNode->_pParent;
while (_pNode == pParent->_pLeft)
{
_pNode = pParent;
pParent = _pNode->_pParent;
}
_pNode = pParent;
}
}
4.3.2 改造红黑树
RBTree.h
// 因为关联式容器中存储的是<key, value>的键值对,因此
// k为key的类型,
// ValueType: 如果是map,则为pair<K, V>; 如果是set,则为k
// KeyOfValue: 通过value来获取key的一个仿函数类
#pragma once
#include<vector>
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 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;
}
Self& operator++()
{
if (_node->_right)
{
// 下一个,右树最左节点
Node* leftMin = _node->_right;
while (leftMin->_left)
{
leftMin = leftMin->_left;
}
_node = leftMin;
}
else
{
// 下一个,孩子等于父亲左的那个祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right)
{
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
};
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*> ConstIterator;
RBTree() = default;
RBTree(const RBTree<K, T, KeyOfT>&t)
{
_root = Copy(t._root);
}
// t2 = t1
RBTree<K, T, KeyOfT>& operator=(RBTree<K, T, KeyOfT> t)
{
swap(_root, t._root);
return *this;
}
~RBTree()
{
Destroy(_root);
_root = nullptr;
}
Iterator Begin()
{
Node* leftMin = _root;
while (leftMin && leftMin->_left)
{
leftMin = leftMin->_left;
}
return Iterator(leftMin);
}
Iterator End()
{
return Iterator(nullptr);
}
ConstIterator End() const
{
return ConstIterator(nullptr);
}
ConstIterator Begin() const
{
Node* leftMin = _root;
while (leftMin && leftMin->_left)
{
leftMin = leftMin->_left;
}
return ConstIterator(leftMin);
}
Iterator Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
cur = cur->_right;
}
else if (cur->_key > key)
{
cur = cur->_left;
}
else
{
return Iterator(cur);
}
}
return End();
}
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)
{
// K
// pair<K, V>
// kot对象,是用来取T类型的data对象中的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; // 新增节点给红色
if (kot(parent->_data) < kot(data))
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
cur->_parent = parent;
// parent的颜色是黑色也结束
while (parent && parent->_col == RED)
{
// 关键看叔叔
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
// 叔叔存在且为红,-》变色即可
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
// 继续往上处理
cur = grandfather;
parent = cur->_parent;
}
else // 叔叔不存在,或者存在且为黑
{
if (cur == parent->_left)
{
// g
// p u
// c
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
// g
// p u
// c
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
else
{
Node* uncle = grandfather->_left;
// 叔叔存在且为红,-》变色即可
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
// 继续往上处理
cur = grandfather;
parent = cur->_parent;
}
else // 叔叔不存在,或者存在且为黑
{
// 情况二:叔叔不存在或者存在且为黑
// 旋转+变色
// g
// u p
// c
if (cur == parent->_right)
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
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);
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
subL->_right = parent;
Node* ppNode = parent->_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;
}
}
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
subR->_left = parent;
Node* ppNode = parent->_parent;
parent->_parent = subR;
if (parent == _root)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
if (ppNode->_right == parent)
{
ppNode->_right = subR;
}
else
{
ppNode->_left = subR;
}
subR->_parent = ppNode;
}
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
bool IsBalance()
{
if (_root->_col == RED)
{
return false;
}
int refNum = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK)
{
++refNum;
}
cur = cur->_left;
}
return Check(_root, 0, refNum);
}
private:
Node* Copy(Node* root)
{
if (root == nullptr)
return nullptr;
Node* newroot = new Node(root->_data);
newroot->_col = root->_col;
newroot->_left = Copy(root->_left);
if (newroot->_left)
newroot->_left->_parent = newroot;
newroot->_right = Copy(root->_right);
if (newroot->_right)
newroot->_right->_parent = newroot;
return newroot;
}
void Destroy(Node* root)
{
if (root == nullptr)
return;
Destroy(root->_left);
Destroy(root->_right);
delete root;
root = nullptr;
}
bool Check(Node* root, int blackNum, const int refNum)
{
if (root == nullptr)
{
//cout << blackNum << endl;
if (refNum != blackNum)
{
cout << "存在黑色节点的数量不相等的路径" << endl;
return false;
}
return true;
}
if (root->_col == RED && root->_parent->_col == RED)
{
cout << root->_kv.first << "存在连续的红色节点" << endl;
return false;
}
if (root->_col == BLACK)
{
blackNum++;
}
return Check(root->_left, blackNum, refNum)
&& Check(root->_right, blackNum, refNum);
}
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_InOrder(root->_right);
}
private:
Node* _root = nullptr;
//size_t _size = 0;
};
4.3.3 map的模拟实现
map的底层结构就是红黑树,因此在map中直接封装一棵红黑树,然后将其接口包装下即可
Mymap.h
#pragma once
namespace dc
{
template<class K, class V>
class map
{
struct MapKeyOfT
{
const K& operator()(const pair<K, V>& kv)
{
return kv.first;
}
};
public:
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::Iterator iterator;
typedef typename RBTree<K, const K, MapKeyOfT>::ConstIterator const_iterator;
const_iterator begin() const
{
return _t.Begin();
}
const_iterator end() const
{
return _t.End();
}
iterator begin()
{
return _t.Begin();
}
iterator end()
{
return _t.End();
}
//bool insert(const pair<K, V>& kv)
//{
// return _t.Insert(kv);
//}
iterator find(const K& key)
{
return _t.Find(key);
}
pair<iterator, bool> insert(const pair<K, V>& kv)
{
return _t.Insert(kv);
}
V& operator[](const K& key)
{
pair<iterator, bool> ret = _t.Insert(make_pair(key, V()));
return ret.first->second;
}
private:
RBTree<K, pair<const K, V>, MapKeyOfT> _t;
};
}
4.3.4 set的模拟实现
set的底层为红黑树,因此只需在set内部封装一棵红黑树,即可将该容器实现出来(具体实现可参考map)
Myset.h
#pragma once
namespace dc
{
template<class K>
class set
{
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
typedef typename RBTree<K, const K, SetKeyOfT>::Iterator iterator;
typedef typename RBTree<K, const K, SetKeyOfT>::ConstIterator const_iterator;
const_iterator begin() const
{
return _t.Begin();
}
const_iterator end() const
{
return _t.End();
}
iterator begin()
{
return _t.Begin();
}
iterator end()
{
return _t.End();
}
iterator find(const K& key)
{
return _t.Find(key);
}
pair<iterator, bool> insert(const K& key)
{
return _t.Insert(key);
}
//bool insert(const K& key)
//{
// return _t.Insert(key);
//}
private:
RBTree<K, const K, SetKeyOfT> _t;
};
}