一、map、set的底层结构
前面对map、set等树形结构的关联式容器进行了简单的介绍,了解到map、set都是由红黑树封装实现的。红黑树是一种由二叉搜索树进行平衡处理后的平衡树,其查找、插入、删除等操作的时间复杂度为O(logn),详情请参考数据结构——红黑树详解-CSDN博客。
红黑树的类模板参数value
map、set底层都是使用红黑树进行封装,set内数据为key,只要传递K一个类模板参数;而map内数据为pair<key-value>键值对,需要传递K,V两个类模板参数给红黑树。那怎么解决这个问题呢?
观察上图,set的类模板参数列表中只有一个Key,在内部将Key取别名为value_type。map的类模板参数列表中有Key、T,则是将Key、T构造为键值对后起别名为value_type。都将value_type类型传给红黑树的模板类参数列表,即由传递的value_type决定红黑树节点是set的key还是map的key-value,实现红黑树的泛型编程。
红黑树的类模板参数KeyOfValue
由于红黑数内数据类型的不确定,在查找,插入,删除等操作中,需要先取key值,再进行比较。所以我们需要向红黑树中传递KeyOfValue仿函数来进行取出key值的操作。
struct mapkeyofT {const k& operator()(const pair<k, v>& kv) {return kv.first;}};
struct setkeyofT {const k& operator()(const k& key) {return key;}};
二、红黑树的迭代器实现
红黑树迭代器的本质就是红黑树的结点指针,要将红黑树中的元素升序遍历,我们采用中序遍历的方式来重载迭代器的++和--操作。
2.1 operator++与operator--
operator++(左子树、根、右子树)就是找到下一个只比当前元素大的元素。
如果当前节点右子树不为空,去找右子树中的最小值,即找到右子树的最左节点。
如果当前节点右子树为空,说明以当前节点为根节点的树中序遍历结束。依次向上查找直到祖先节点(parent)中左子树为cur的节点。
//按照中序遍历的方式,++self& operator++() {//如果右子树不为空,那么比这个元素大的下一个节点就是右子树的最小元素//也就是右子树的最左节点if (_node->_right){Node* subleft = _node->_right;while (subleft->_left){subleft = subleft->_left;}_node = subleft;}else {//右子树为空,(以当前节点为根节点的树中序遍历结束)向上查找//下一个节点为祖先节点(parent)中左子树为cur的节点Node* cur = _node;Node* parent = cur->_parent;while (parent && parent->_right == cur) {//cur依旧为parent的右子树,说明更新后的cur中序遍历也结束了cur = parent;parent = cur->_parent;}_node = parent;}return *this;}
operator--(右子树、根、左子树)就是找到下一个只比当前元素小的元素。
如果当前节点左子树不为空,去找左子树中的最大值,即找到左子树的最右节点。
如果当前节点左子树为空,说明以当前节点为根节点的树中序遍历结束。依次向上查找直到祖先节点(parent)中右子树为cur的节点。
//如果左子树不为空,那么比这个元素大的下一个节点就是左子树的最大元素//也就是左子树的最右节点self& operator--() {if (_node->_left) {Node* subright = _node->_left;while (subright->_right) {subright = subright->_right;}_node = subright;}else{//右子树为空,(以当前节点为根节点的树中序遍历结束)向上查找//下一个节点为祖先节点(parent)中右子树为cur的节点Node* cur = _node;Node* parent = cur->_parent;while (parent && parent->_left == cur) {cur = parent;parent = parent->_parent;}_node = parent;}}
2.2 operator==与operator!=
bool operator !=(const self& s) {return _node != s._node;}bool operator==(const self& s) {return _node = s._node;}
2.3 operator*与operator->
operator*()返回迭代器指向节点的数据,operator->()返回迭代器指向节点的数据的地址。
T& operator*() {return _node->_data;}T* operator->() {return &_node->_data;}
完整迭代器实现如下:
template<class T>
struct RBTreeIterator
{typedef RBTreeNode<T> Node;typedef RBTreeIterator<T> self;Node* _node;RBTreeIterator(Node* node):_node(node){}//运算符重载:那个对象调用符号,传那个对象的this指针T& operator*() {return _node->_data;}T* operator->() {return &_node->_data;}//按照中序遍历的方式,++self& operator++() {//如果右子树不为空,那么比这个元素大的下一个节点就是右子树的最小元素//也就是右子树的最左节点if (_node->_right){Node* subleft = _node->_right;while (subleft->_left){subleft = subleft->_left;}_node = subleft;}else {//右子树为空,(以当前节点为根节点的树中序遍历结束)向上查找//下一个节点为祖先节点(parent)中左子树为cur的节点Node* cur = _node;Node* parent = cur->_parent;while (parent && parent->_right == cur) {cur = parent;parent = cur->_parent;}_node = parent;}return *this;}//如果左子树不为空,那么比这个元素大的下一个节点就是左子树的最大元素//也就是左子树的最右节点self& operator--() {if (_node->_left) {Node* subright = _node->_left;while (subright->_right) {subright = subright->_right;}_node = subright;}else{//右子树为空,(以当前节点为根节点的树中序遍历结束)向上查找//下一个节点为祖先节点(parent)中右子树为cur的节点Node* cur = _node;Node* parent = cur->_parent;while (parent && parent->_left == cur) {cur = parent;parent = parent->_parent;}_node = parent;}}bool operator !=(const self& s) {return _node != s._node;}bool operator==(const self& s) {return _node = s._node;}
};
三、红黑树的更改
之前已经对红黑树模拟实现,现在需要对红黑树进行一些更改方便map、set的封装。
3.1 红黑树的迭代器的封装
typedef RBTreeIterator<T> iterator;
begin()与end()函数实现
begin()应该是一棵红黑树的最小节点,即最左节点;end()为nullptr。
iterator begin() {Node* subleft = _root;while (subleft && subleft->_left) {subleft = subleft->_left;}return iterator(subleft);}iterator end()
{return iterator(nullptr);
}
3.2 红黑树的查找、插入
在红黑树的查找、插入操作中,需要先实例化一个keyofT对象使用仿函数取出key值,再比较大小。另外,为了map的operator[],红黑树的插入函数返回值需要更改为<Iterator,bool>的键对值。
pair<iterator, bool> Insert(const T& data){if (_root == nullptr) {_root = new Node(data);_root->_col = BLACK;Node* newnode = _root;return make_pair(iterator(newnode), true);}keyofT kot;Node* parent = nullptr;Node* cur = _root;while (cur) {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); // 在cur位置插入红色节点Node* newnode = cur;if (kot(parent->_data) < kot(data)) {//连接cur节点到红黑树parent->_right = cur;}else {parent->_left = cur;}cur->_parent = parent;return make_pair(iterator(newnode), true);;}
上述为红黑树插入函数,忽略旋转、变色操作。
3.3 红黑树更改后代码实现
#define _CRT_SECURE_NO_WARNINGS
#pragma once
#include<vector>
#include<iostream>
#include<time.h>
using namespace std;enum Colour {//枚举类型,红黑树颜色RED,BLACK
};template<class T>//T决定节点是key还是k-v类型
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>
struct RBTreeIterator
{typedef RBTreeNode<T> Node;typedef RBTreeIterator<T> self;Node* _node;RBTreeIterator(Node* node):_node(node){}//运算符重载:那个对象调用符号,传那个对象的this指针T& operator*() {return _node->_data;}T* operator->() {return &_node->_data;}//按照中序遍历的方式,++self& operator++() {//如果右子树不为空,那么比这个元素大的下一个节点就是右子树的最小元素//也就是右子树的最左节点if (_node->_right){Node* subleft = _node->_right;while (subleft->_left){subleft = subleft->_left;}_node = subleft;}else {//右子树为空,(以当前节点为根节点的树中序遍历结束)向上查找//下一个节点为祖先节点(parent)中左子树为cur的节点Node* cur = _node;Node* parent = cur->_parent;while (parent && parent->_right == cur) {cur = parent;parent = cur->_parent;}_node = parent;}return *this;}//如果左子树不为空,那么比这个元素大的下一个节点就是左子树的最大元素//也就是左子树的最右节点self& operator--() {if (_node->_left) {Node* subright = _node->_left;while (subright->_right) {subright = subright->_right;}_node = subright;}else{//右子树为空,(以当前节点为根节点的树中序遍历结束)向上查找//下一个节点为祖先节点(parent)中右子树为cur的节点Node* cur = _node;Node* parent = cur->_parent;while (parent && parent->_left == cur) {cur = parent;parent = parent->_parent;}_node = parent;}}bool operator !=(const self& s) {return _node != s._node;}bool operator==(const self& s) {return _node = s._node;}
};template<class k, class T, class keyofT>
class RBTree
{typedef RBTreeNode<T> Node;
public:typedef RBTreeIterator<T> iterator;iterator begin() {Node* subleft = _root;while (subleft && subleft->_left) {subleft = subleft->_left;}return iterator(subleft);}iterator end(){return iterator(nullptr);}pair<iterator, bool> Insert(const T& data){if (_root == nullptr) {_root = new Node(data);_root->_col = BLACK;Node* newnode = _root;return make_pair(iterator(newnode), true);}keyofT kot;Node* parent = nullptr;Node* cur = _root;while (cur) {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); // 在cur位置插入红色节点Node* newnode = cur;if (kot(parent->_data) < kot(data)) {//连接cur节点到红黑树parent->_right = cur;}else {parent->_left = cur;}cur->_parent = parent;while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* uncle = grandfather->_right;//1:当叔叔存在且为红if (uncle && uncle->_col == RED){//变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//继续向上处理cur = grandfather;parent = cur->_parent;}else{//2:叔叔不存在或者存在且为黑,由第一种情况调整而来//旋转+变色if (cur == parent->_left){// g// p u// c//此时路径上黑色节点不相同,且有连续的红色节点,不满足最长路径是最短路径的两倍RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else {// g// p u// cRotateL(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// cif (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else {// g// u p// cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return make_pair(iterator(newnode), true);;}void RotateL(Node* parent)//左单旋{++rotateSize;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->_parent = subR;if (parent == _root)//考虑parent是该树的根节点{_root = subR;subR->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subR;}else{ppnode->_right = subR;}subR->_parent = ppnode;}}void RotateR(Node* parent)//右单旋{++rotateSize;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;subL->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subL;}else{ppnode->_right = subL;}subL->_parent = ppnode;}}size_t Size(){return _Size(_root);}size_t _Size(Node* root){if (root == NULL)return 0;return _Size(root->_left)+ _Size(root->_right) + 1;}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.first << endl;_InOrder(root->_right);}void InOrder(){_InOrder(_root);}int GetRotateSize(){return rotateSize;}int _Height(Node* root){if (root == nullptr)return 0;int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;}int Height(){return _Height(_root);}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 NULL;}bool IsBalance(){if (_root && _root->_col == RED)return false;int refBlackNum = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK)refBlackNum++;cur = cur->_left;}return Check(_root, 0, refBlackNum);}bool Check(Node* cur, int blackNum, int refBlackNum){//传blackNum,每次走到黑色节点++,为nullptr回到上一层调用走右子树,此时blackNum还是上一层的值if (cur == nullptr) {if (refBlackNum != blackNum) {cout << "黑色节点的数量不相等" << endl;return false;}return true;}if (cur->_col == RED && cur->_parent->_col == RED) {cout << "存在连续的红色节点" << endl;return false;}if (cur->_col == BLACK)++blackNum;return Check(cur->_left, blackNum, refBlackNum) && Check(cur->_right, blackNum, refBlackNum);}
private:Node* _root = nullptr;int rotateSize = 0;};
四、map、set封装红黑树
4.1 set封装红黑树
set内部成员变量为实例化的红黑树对象,插入、查找等成员函数为调用红黑树对应函数。
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();}pair<iterator, bool> insert(const k& key){return _t.Insert(key);}private:RBTree<k, k, setkeyofT> _t;};
4.2 map封装红黑树
map内部成员变量为实例化的红黑树对象,插入、查找等成员函数为调用红黑树对应函数。
与set不同的是,map多了operator[]重载,它会在内部调用map的insert函数。
map的operator[]重载
v& operator[](const k& key){pair<iterator, bool> ret = Insert(make_pair(key, v()));return ret.first->second;//key对应节点迭代器指向的数据value}
insert函数会返回插入元素的迭代器位置和元素是否存在的bool值构成的pair对象,在operator[]函数中,会先让map对象调用insert函数插入键值k和value的默认值(为value类型的默认构造函数生成的临时对象)所构成的pair对象。
其中ret.first就是获取插入后元素的迭代器,ret.first->second获得key所对应的value值。
当key存在时,插入失败。获取key所在pair对象的迭代器,ret.first->second为key所对应的value。
当key不存在时,插入成功。获取新插入pair对象的迭代器,ret.first->second为默认value。
所以,map的operator[]能够实现查找,修改,插入三个功能。
map封装红黑树实现
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, pair<const k, v>, mapkeyofT>::iterator是一个类型而不是一个成员。iterator begin() {return _t.begin();}iterator end() {return _t.end();}pair<iterator, bool> insert(const pair<k, v>& kv){return _t.Insert(kv);}v& operator[](const k& key){pair<iterator, bool> ret = Insert(make_pair(key, v()));return ret.first->second;//key对应节点迭代器指向的数据value}private:RBTree<k, pair<const k, v>, mapkeyofT> _t;};