一、map和set的使用
1. 关联式容器
在初阶阶段,我们已经接触过STL中的部分容器,比如:vector、list、deque、forward_list(C++11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。那什么是关联式容器?它与序列式容器有什么区别?
关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高。
💕 树形结构的关联式容器:
根据应用场景的不桶,STL总共实现了两种不同结构的管理式容器:树型结构与哈希结构。树型结构的关联式容器主要有四种:map
、set
、multimap
、multiset
。这四种容器的共同点是:使用平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列。下面一依次介绍每一个容器。
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)
{}
};
这里我们可以看到,pair类中的first就是键值key,second就是key对应的信息 value;所以以后在定义KV模型的时候,只需要在容器/容器中的每一个节点中定义一个pair对象即可。
💕 make_pair函数
由于 pair 是类模板,所以我们通常是以 显式实例化 + 匿名对象 的方式来进行使用,但是由于显式实例化比较麻烦,所以 C++ 还提供了 make_pair 函数,其定义如下:
make_pair函数的内部实现如下:
template <class T1,class T2>
pair<T1,T2> make_pair (T1 x, T2 y)
{
return ( pair<T1,T2>(x,y) );
}
make_pair 返回的是一个 pair 的匿名对象,匿名对象会自动调用 pair 的默认构造完成初始化;但由于 make_pair 是一个函数模板,所以模板参数的类型可以根据实参来自动推导完成隐式实例化,这样我们就不用每次都显式指明参数类型了。
3. 关联式容器的使用
3.1 set
set是一种
key
模型的容器,也就是说,set中只有键值key,而没有对应的value,并且每个key都是唯一的,set容器中的元素不允许修改,因为那样会破坏搜索树的规则。但是set允许插入和删除。
T:
set中存放元素的类型,实际在底层存储<value, value>的键值对。Compare:
set中元素默认按照小于来比较Alloc:
set中元素空间的管理方式,使用STL提供的空间配置器管理
总结:
- set 是K模型的容器,所以 set 中插入元素时,只需要插入 key 即可,不需要构造键值对;
- set中的元素不可以重复,因此可以使用set进行去重;
- 由于 set 底层是搜索树,所以使用 set 的迭代器遍历 set 中的元素,可以得到有序序列,即 set 可以用来排序;
- set 默认使用的仿函数为 less,所以 set 中的元素默认按照小于来比较;
- 由于 set 底层是平衡树搜索树,所以 set 中查找某个元素,时间复杂度为 O(logN);
- set 中的元素不允许修改,因为这可能破坏搜索树的结构;
- set 中的底层使用平衡二叉搜索树 (红黑树) 来实现。
💕 set的使用
构造
迭代器
修改
其他操作
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[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;
}
3.2 multiset
multiset 也是 K模型 的容器,它和 set 唯一的区别在于
multiset 中允许存在重复的 key 值节点
,所以 multiset 可以用来排序和查找,但是不能用来去重。
💕 multiset的使用
multiset 的使用和 set 几乎一样,这里我们需要注意两点即可:
- 由于 multiset 中允许存在重复 key 值的节点,所以 multiset 中
count
函数就有作用了,我们可以通过 count 函数来统计同一 key 中在 multiset 中的数量。- multiset 中
find
函数的使用也和 set 有所区别 – 由于 set 中没有重复的节点,所以 find 时要么返回该节点位置的迭代器,要么返回 end();而 multiset 中可能有重复的节点,所以 find 时返回的是同一 key 值中的哪一个节点呢?实际上 find 返回的是中序遍历过程中第一个匹配的节点位置的迭代器。
multiset的使用范例
void multiset_test() {
// 构造multiset
int array[] = { 1, 3, 5, 7, 3, 2, 4, 6, 8, 0, 1, 3, 5, 7, 3, 2, 4, 6, 8, 3 };
multiset<int> s(array, array + sizeof(array) / sizeof(array[0]));
cout << s.size() << endl;
// 正向打印multiset中的元素,multiset允许重复元素的出现
for (const auto& e : s)
cout << e << " ";
cout << endl;
//如果查找的key在multiset中有多个,则返回中序遍历中遇到的第一个节点的迭代器
multiset<int>::iterator pos = s.find(3);
while (pos != s.end()) {
cout << *pos << " ";
++pos;
}
cout << endl;
// 查找+删除
cout << s.count(3) << endl; //输出key为3的节点的个数
pos = s.find(3);
if (pos != s.end())
s.erase(pos);
cout << s.count(3) << endl;
}
3.3 map
map
和 set 一样都是按照一定次序存储元素的容器,其底层也是一棵平衡二叉搜索树;和 set 不同的是,map 是 KV模型 的容器,在map 中,键值 key 通常用于排序和惟一地标识元素,而值 value中 用于存储与此键值 key 关联的内容,键值 key 和值value的类型可以不同; 在 map 内部,key-value 通过成员类型 pair 绑定在一起,也就是我们文章开始提到的键值对;
需要注意的是:map 中的元素是按照键值 key 进行比较排序的,而与 key 对应的 value 无关,同时,map 中也不允许有重复 key 值的节点,也不允许修改key,但是它可以修改节点中key对应的value;map 也可用于排序、查找和去重,且 map 查找的时间复杂度也为 O(logN)。
💕 map的使用
构造
迭代器
修改
元素访问
[ ]
运算符重载的函数定义如下:
mapped_type& operator[] (const key_type& k)
{
(*((this->insert(make_pair(k, mapped_type()))).first)).second;
}
//拆解后的函数
V& operator[] (const K& key)
{
pair<iterator, bool> ret = insert(make_pair(key, V()));
//return *(ret.first)->second;
return ret.first->second;
}
下面我们来分析一下这个函数:
- operator[] 函数先向map容器中插入key:如果map中有与该值相等的节点,则插入失败,返回的pair中存放着该节点位置的迭代器和false;如果map中没有与该节点相等的节点,则插入成功,该节点的value值为 V 默认构造的缺省值。
- 然后,operator[] 会取出pair迭代器(ret,first)然后对迭代器进行解引用得到一个pair<k,v>对象,最后再返回排pair对象中的second的引用,即key对应的value的引用。因此我们可以直接在函数外部修改key对应的value的值。
map的使用案例
void map_test() {
map<string, string> m;
m.insert(pair<string, string>("peach", "桃子"));
//为了方便,我们建议直接用make_pair函数来构造键值对
m.insert(make_pair("banan", "香蕉"));
m["apple"] = "苹果";
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.4 multimap
同样,multimap与map的区别和multiset与set的区别是一样的。find返回的是中序遍历中遇到的第一个节点的迭代器。count返回和key值相等的节点的个数。
这里我们需要注意的是:multimap中没有重载[]运算符,因为multimap中的key值是可以重复的。如果使用 [] 运算符,会导致无法确定访问哪个哪一个元素。
二、map和set的模拟实现
我们知道,map和set容器的底层都是使用红黑树实现的。但是,set是K模型的的容器,而map是KV模型的容器,他们却都能使用红黑树来封装。下面,我们先来看一下源码中是如何实现的。
这里我们发现,set头文件中包含了 stl_set.h
和 stl_multiset.h
,而map头文件中包含了 stl_map.h
和 stl_multimap.h
,而stl_tree.h
应该就是用来封装实现他们的红黑树。
这里我们可以看到: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 的类型。
下面我们来看一下红黑树的源码,看一下为什么对于这种K模型还需要设计一个value_type变量呢?
//stl_tree.h
//红黑树的基类
struct __rb_tree_node_base
{
typedef __rb_tree_color_type color_type;
typedef __rb_tree_node_base* base_ptr;
color_type color;
base_ptr parent;
base_ptr left;
base_ptr right;
};
//红黑树的节点结构
template <class Value>
struct __rb_tree_node : public __rb_tree_node_base
{
typedef __rb_tree_node<Value>* link_type;
Value value_field;
};
//红黑树
template <class Key, class Value, class KeyOfValue, class Compare,
class Alloc = alloc>
class rb_tree {
protected:
typedef __rb_tree_node_base* base_ptr;
typedef __rb_tree_node<Value> rb_tree_node;
public:
typedef Key key_type;
typedef Value value_type;
typedef rb_tree_node* link_type;
}
set、map
和rbtree
之间的关系:
通过这张图我们就可以理解,为什么set和map是不同模型的容器,但是他们却都可以使用同一棵树红黑树作为底层容器了。
如果是 map,则红黑树的模板参数 _Val 被实例化为 pair\u003CK,V>,那么红黑树节点中存储的数据的类型也是 pair\u003CK,V>;如果是 set,则红黑树的模板参数 _Val 被实例化为 K,此时红黑树节点中存储的数据的类型就为 K;
也就是说,红黑树完全可以根据第二个模板参数 _Val 的实参类型的不同实例化出符合 set 要求和 符合 map 要求的不同的两个类。这里我们来解答一下传递第一个模板参数 _Key 的作用?
这是因为某些函数需要使用 key,比如 find 函数就只能通过 key 进行查询,即在 map 的 find 中不能通过 _Val 即 pair 类型来进行查询。
1. 红黑树的实现(封装map和set版本)
现在,我们需要参考源码中的方式将我们自己实现的红黑树改造一下,使得它既能够封装map,又能够封装set。
1.1 节点的实现
//创建红黑树的节点
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 K, class T>
class RBTree
{
typedef RBTreeNode<T> Node;
private:
Node* _root = nullptr;
};
1.2 KeyOfT(仿函数)
我们在 insert 时需要比较 key 的大小来确定插入位置,在之前模拟实现的红黑树中,我们直接将节点定义为了pair<K,V>
类型,所以我们可以直接取 kv.first 进行比较;但是现在,节点中的数据既可能是 pair 类型,也可能是 key 的类型,所以我们不能再用 data.first,同时由于stl 中实现的 pair 比较函数并不是单纯比较 first,而是先比较 first,再比较 second。
为了实现在插入操作中既可以比较Key模型
的Key值,又可以比较KV模型
的Key值,我们可以自己写一个仿函数来完成红黑树中节点数据 _data大小的比较了。当然,源码中也是这样实现的。
//set
namespace cjl
{
template<class K>
class set
{
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
private:
RBTree<K, K, SetKeyOfT> _t;
};
}
//map
namespace cjl
{
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;
};
}
1.3 红黑树的插入Insert
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; //修改父节点指向
//调整节点
//...
return true;
}
private:
Node* _root;
};
1.4 迭代器iterator
💕 begin() 迭代器
begin()迭代器的位置是红黑树中序遍历的第一个节点,也就是整棵树的最左节点。
💕 end() 迭代器
end()迭代器是红黑树中序遍历最后一个元素的下一个位置。为了让 end() 能够指向最右节点的下一个节点, stl 源码中增加了一个哨兵位的头结点,该节点的 left 指向这棵树的最左节点,也就是begin(),right 指向这棵树的最右节点,parent 指向 nullptr,然后让根的父节点指向它,最右节点的 right 也指向它;所以在 stl 源码的实现中这个哨兵位的头结点就是 end();
这里我们直接将end()定义为nullptr即可。
💕 迭代器的++ 与 - -
因为红黑树的中序遍历是一个有序序列,所以++
就是跳转到红黑树中序遍历的下一个节点的位置。--
则是反过来跳转到中序遍历中上一个节点的位置。
+ +情况的分类
- 若当前节点有右孩子,那么迭代器++跳转到右子树的最左节点。
- 若当前节点没有右孩子,则迭代器++跳转到当前节点为父节点
- - 情况的分类
- 若当前节点有左孩子,那么迭代器 - - 跳到左子树的最右节点;
- 若当前节点没有左孩子,则迭代器 - - 跳到 当前节点为 父节点的右孩子 的那一个祖先节点;
//迭代器的实现
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;
}
//重载 ++it
Self& operator++()
{
//本质上是找中序遍历的下一个元素——分为两种情况:right == nullptr 和 right != nullptr
if (_node->_right) // right != nullptr
{
Node* parent = _node->_right;
while (parent->_left)
{
parent = parent->_left;
}
_node = parent;
}
else //right == nullptr
{
Node* parent = _node->_parent;
Node* cur = _node;
while (parent && cur == parent->_right)
{
cur = cur->_parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
//重载 --it
Self& operator--()
{
if (_node->_left) // left != nullptr
{
Node* parent = _node->_left;
while (parent->_right)
{
parent = parent->_right;
}
_node = parent;
}
else //left == nullptr
{
Node* parent = _node->_parent;
Node* cur = _node;
while (parent && cur == parent->_left)
{
cur = cur->_parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
};
2. set的模拟实现
set 的插入
、查询
函数直接使用适配器模式,调用红黑树对应的函数即可完成,这里最重要的是迭代器的复用。
当我们直接复用红黑树的迭代器时,会发现一个问题,我们可以使用迭代器来修改key值,但是如果这样的话必定会破坏搜索树的结构。
我们直接让 set 的普通迭代器和 const 迭代器都使用红黑树的 const 迭代器来进行封装即可,这样 set 迭代器解引用得到的就是 const K& 了;但是这样做之后发现虽然的确不能通过迭代器来修改 key 的值了,但是这里又发生了新的错误:
其实这是因为在insert中返回的是红黑树的普通迭代器,但是接受返回值的却是红黑树的const迭代器。
要解决这个问题的方法其实也并不难,我们需要在红黑树的迭代器中实现一个拷贝构造函数。
- 当模板实例化为 <T, T&, T*> 时,iterator 和 Self 相同,此时这是一个拷贝构造函数;
- 当模板实例化为 <T, const T&, const T*> 时,Self 是 const 迭代器,而 iterator 是普通迭代器,此时这是一个构造函数,它可以用普通迭代器构造出一个 const 迭代器。
所以我们可以通过红黑树迭代器类中构造函数来实现用普通迭代器来构造 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)
{}
__RBTreeIterator(const __RBTreeIterator<T,T&,T*>& iterator)
:_node(iterator._node)
{}
//运算符的重载...
};
💕 set模拟实现源码:
namespace cjl
{
template<class K>
class set
{
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
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();
}
//插入
pair<iterator, bool> insert(const K& key)
{
return _t.Insert(key);
}
//查询
iterator find(const K& key)
{
return _t.Find(key);
}
public:
private:
RBTree<K, K, SetKeyOfT> _t;
};
}
3. map的模拟实现
namespace cjl
{
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;
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();
}
pair<iterator,bool> insert(const pair<const K, V>& kv)
{
return _t.Insert(kv);
}
iterator find(const K& key)
{
return _t.Find(key);
}
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. RbTree的完整源码
#pragma once
//枚举类型,枚举红黑树的颜色红色和黑色
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)
{}
__RBTreeIterator(const __RBTreeIterator<T,T&,T*>& iterator)
:_node(iterator._node)
{}
//重载*
Ref operator*()
{
return _node->_data;
}
//重载->
Ptr operator->()
{
return &(_node->_data);
}
//重载!=
bool operator!=(const Self& s)
{
return _node != s._node;
}
//重载 ++it
Self& operator++()
{
//本质上是找中序遍历的下一个元素——分为两种情况:right == nullptr 和 right != nullptr
if (_node->_right) // right != nullptr
{
Node* parent = _node->_right;
while (parent->_left)
{
parent = parent->_left;
}
_node = parent;
}
else //right == nullptr
{
Node* parent = _node->_parent;
Node* cur = _node;
while (parent && cur == parent->_right)
{
cur = cur->_parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
//重载 --it
Self& operator--()
{
if (_node->_left) // left != nullptr
{
Node* parent = _node->_left;
while (parent->_right)
{
parent = parent->_right;
}
_node = parent;
}
else //left == nullptr
{
Node* parent = _node->_parent;
Node* cur = _node;
while (parent && cur == parent->_left)
{
cur = cur->_parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
};
//创建红黑树
template<class K, class T ,class KeyOfT>
class RBTree
{
typedef RBTreeNode<T> Node;
public:
//析构
~RBTree()
{
_Destroy(_root);
_root = nullptr;
}
//迭代器的实现
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);
}
const_iterator begin() const
{
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return iterator(cur);
}
const_iterator end() const
{
return iterator(nullptr);
}
//查询
iterator Find(const K& key)
{
KeyOfT kot;
Node* cur = _root;
while (cur)
{
if (key > kot(cur->_data))
cur = cur->_right;
else if (key < kot(cur->_data))
cur = cur->_left;
else
return iterator(cur);
}
return iterator(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(data) > kot(cur->_data))
{
parent = cur;
cur = cur->_right;
}
else if (kot(data) < kot(cur->_data))
{
parent = cur;
cur = cur->_left;
}
else
{
return make_pair(iterator(cur), false);
}
}
//链接节点
cur = new Node(data);
Node* newnode = cur;
if (kot(parent->_data) > kot(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;
if (uncle && uncle->_col == RED)// 情况1:u存在且为红,变色处理,并继续往上处理
{
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
//继续向上调整
cur = grandfather;
parent = cur->_parent;
}
else // 情况2 + 3:u不存在/u存在且为黑 --- 旋转 + 变色
{
// g
// p u
// c
if (cur == parent->_left)
{
//右单旋
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
// g
// p u
// c
//先对parent节点进行左单旋,在对grandfather进行右单旋
RotateL(parent);
RotateR(grandfather);
//调整颜色
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
else // (grandfather->_right == parent)
{
// u存在且为红,变色处理,并继续往上处理
// g
// u p
// c
Node* uncle = grandfather->_left;
if (uncle && uncle->_col == RED)
{
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_parent;
}
else
{
// g
// u p
// c
if (cur == parent->_left)
{
RotateR(parent);
RotateL(grandfather);
//调整颜色
cur->_col = BLACK;
grandfather->_col = RED;
}
else
{
// g
// u p
// c
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return make_pair(iterator(newnode), true);
}
//中序遍历
void InOrder()
{
_InOrder(_root);
cout << endl;
}
//判断是否为红黑树
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);
}
int Height()
{
return _Height(_root);
}
private:
//红黑树的高度计算
int _Height(Node* root)
{
if (root == nullptr)
return 0;
int leftHight = _Height(root->_left);
int rightHight = _Height(root->_right);
return leftHight > rightHight ? leftHight + 1 : rightHight + 1;
}
bool _Check(Node* root, int blackNum, int benchmark)
{
if (root == nullptr)
{
if (blackNum != benchmark)
{
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);
}
void _Destroy(Node* root)
{
if (root == nullptr)
return;
_Destroy(root->_left);
_Destroy(root->_right);
delete root;
}
void _InOrder(Node* root)
{
if (root == nullptr)
return;
KeyOfT kot;
_InOrder(root->_left);
cout << kot(root->_data) << " ";
_InOrder(root->_right);
}
//左单旋
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
//这里我们需要注意一下,subRL有可能为空
if (subRL)
subRL->_parent = parent;
Node* ppnode = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
//判断父节点是否为_root节点
if (parent == _root) {
_root = subR;
_root->_parent = nullptr;
}
else {
if (parent == ppnode->_left)
ppnode->_left = subR;
else
ppnode->_right = subR;
subR->_parent = ppnode;
}
}
//右单旋
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
//保存parent节点的_parent节点
Node* ppnode = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
//判断parent节点是否为_root节点
if (parent == _root)
{
_root = subL;
_root->_parent = nullptr;
}
else
{
if (parent == ppnode->_left)
ppnode->_left = subL;
else
ppnode->_right = subL;
subL->_parent = ppnode;
}
}
private:
Node* _root = nullptr;
};