文章目录
- 📕 概念
- 📕 实现
- 框架
- Find()
- ★ 迭代器 ★
- 反向迭代器
- map 的 operator[ ]
- 📕 源代码
- rb_tree.h
- set.h
- map.h
📕 概念
map、set 是 C++ 中的关联式容器,由于 map 和set所开放的各种操作接口,RB-tree 也都提供了,所以几乎所有的 map 和set的操作行为,都只是转调 红黑树 的操作行为。
关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高。
📕 实现
框架
map、set 一个很明显的区别就是,set 存储值,map存储键值对。
set 的结构如下,只需要传入一个模板参数 K ,也就是其实际存储的值的数据类型。复用红黑树的时候,第二个参数就是 K 类型。
#pragma once
#include"rb_tree.h"namespace SetSimulate
{template<class K>class set{struct KeyOfT{const K& operator()(const K& k){return k;}};public:typedef typename RBTree<K, K, KeyOfT>::const_iterator iterator;typedef typename RBTree<K, K, KeyOfT>::const_iterator const_iterator;private:RBTree<K, K, KeyOfT> _t;};
}
而 Map 就不一样了,要传入两个参数,第一个是 K(键值),第二个是 V (映射值)。实际存储的数据类型是 pair<K,V> 。如下,复用红黑树的时候,将其第二个模板参数设为 pair<K,V> 即可。
#pragma once
#include"rb_tree.h"namespace MapSimulate
{template<class K, class V>class map{struct MapOfKey{const K& operator()(const pair<const K, V>& kv){return kv.first;}};public:typedef typename RBTree<K, pair<const K,V>, MapOfKey>::iterator iterator;typedef typename RBTree<K, pair<const K,V>, MapOfKey>::const_iterator const_iterator;typedef typename RBTree<K, pair<const K, V>, MapOfKey>::reverse_iterator reverse_iterator;typedef typename RBTree<K, pair<const K, V>, MapOfKey>::const_reverse_iterator const_reverse_iterator;private:RBTree<K, pair<const K, V>, MapOfKey> _t;};
}
关于 map、set 底层红黑树的第三个模板参数,分析上面的两段代码,实际上是取存储数据的 K 值。即,在 map 里面,由于存储的数据类型是 pair<K,V> ,所以 MapOfKey 的作用是取到键值对里面 K 类型的数据;而 set 里面,由于存储的数据本身就是 K 类型,所以 SetOfKey 直接取到该类型的数据。
至于为什么要这样,这是因为,map、set 是对红黑树的封装,如果仅仅实现 set ,可以不用给红黑树传入第三个模板参数,因为 set 里面存储 K 类型的数据,而红黑树插入、查找、删除等等操作里面,数据之间进行比较,本就是比较的 K 类型的数据,所以是一致的。
但是,map 存储的是 pair<K,V> ,由于map、set 是复用同一份红黑树代码,所以无法将代码直接改成适应 pair<K,V> 的情况(这样就不适应 set 了)。所以,需要有一个统一的视角,来得到map、set 的数据里面的 K 值。
当然,上面两端说起来比较抽象,接下来介绍一下 Find 封装,就不难理解了。
Find()
如下是红黑树底层实现 find 的代码。可以看到它在进行数据之间的比较时,是依靠 KeyOfT 实例化出的对象 kt ,取 kt(cur->_data) 。
很显然,当容器无论为 set 还是 map, 红黑树中 find() 函数都依靠 K 类型的 val 来查找。可是, 红黑树的节点中, _data 存储的数据类型却不一样,map 中存储的是 pair<K,V>,set 中存储的是 K。
所以,这里就依靠红黑树的第三个模板参数 KeyOfT 实例化的对象 kt,其重载的 () 来取到 map、set 的 _data 里面的 K 值。这样就统一了。
template<class T>struct RBTreeNode{RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;T _data;Colour _col;RBTreeNode(const T& val): _left(nullptr), _right(nullptr), _parent(nullptr), _data(val), _col(RED){}};template<class K, class T, class KeyOfT>class RBTree{typedef struct RBTreeNode<T> Node;public:Node* find(const K& val){KeyOfT kt;Node* cur = _root;while (cur){if (val > kt(cur->_data)){cur = cur->_right;}else if (val < kt(cur->_data)){cur = cur->_left;}elsereturn cur;}return nullptr;}private:Node* _root;};
如下,无论是 map 还是 set,都是直接调用红黑树的 find() ,然后封装成迭代器返回。
iterator Find(const K& key){Node* cur = _t.find(key);return iterator(cur);}
★ 迭代器 ★
如下,是迭代器的大体框架,主要依靠红黑树的节点指针来实现。
重载 * 、->、!= 都比较简单,和 list 封装的迭代器类似。但是 ,重载 ++ 和 – 就需要一些独特的思路了!
template<class T,class Ref,class Ptr>struct _RBTreeIterator{typedef RBTreeNode<T> Node;typedef _RBTreeIterator<T, Ref, Ptr> self;_RBTreeIterator(Node* node):_node(node){}// begin() 、 end() 处可见其效果_RBTreeIterator(const _RBTreeIterator<T, T&, T*> &it):_node(it._node){}Ref operator*(){return _node->_data;}bool operator!=(const self& s){return this->_node != s._node;}Ptr operator->(){return &_node->_data;}self& operator++(){// 实现return *this;}self& operator--(){// 实现return *this;}Node* _node;};
operator++()
对红黑树的遍历,和搜索二叉树是一样的,中序遍历。
例如,现在某棵红黑树存储了1到10 的数据,使用迭代器遍历的结果必然是 1、2、3、4、5、6、7、8、9、10。
假设现在将存储 5 这个数据的节点,封装成迭代器 it。进行 ++it 操作之后,无疑 it 内部的节点指针,指向的是数据为 6 的节点。再 ++it,it 内部的结点指针就指向数据为 7 的节点。
很明显,++ 操作,只需要考虑右边部分的结果(上面依次++it,实际上节点指针依次指向 6、7、8、9、10 ),不需要考虑 左边的部分(即 1-5 )。
这样就很清晰了,对于一个中序遍历的结果,对于某一个节点 X 的右边部分,分为两种情况:有数据是 X 的子节点(可能是部分,可能是全部);没有数据是 X 的子节点。例如上面 5 这个结果,其右边部分6到10 里面,可能有数据是5的子节点,也可能没有。
根据这两种情况来写代码。
-
对于第一种情况——右边部分存在 X 的子节点。这就很容易了,由于是中序遍历,当前节点遍历完之后,就需要遍历其右子树。所以 ++ 操作,只要找到其右子树的最小节点即可。
-
对于第二种情况,X 不存在右子树,那么必然是要往上找。如下图,假设一开始是图中左边的状态, 现在有 cur 、p 两个节点指针,而 cur 没有右子树,向上寻找, cur 在 p 的右边,这说明 p 已经遍历过了。
再继续往上找,到了下图右边的状态,此时 cur 在 p 的左边,根据中序遍历规则,下一个要遍历的节点就是 此时的 p。
self& operator++(){// 右子树存在,先去右子树的第一个if (_node->_right){Node* subleft=_node->_right;while (subleft->_left){subleft = subleft->_left;}_node = subleft;}else // 右子树不存在,向上找{Node* cur = _node;Node* parent = _node->_parent;while (parent && parent->_right == cur){cur = cur->_parent;parent = parent->_parent;}_node = parent;}return *this;}
operator–()
而 – 就是和 ++ 反过来了,遍历顺序是:右子树、根、左子树。所以,拿到某个节点 X,要先去看左子树是否存在,不存在则往上找。
- 左子树存在,自然是去寻找左子树中最大的节点。
- 左子树不存在,往上找。如下图,假设当前迭代器封装的是 cur 节点,不存在左子树。 – 操作就应该向上找,在下图左边的状态时, cur 节点是 p节点的左孩子,根据 – 的遍历顺序,p 已经遍历过了。
继续往上,到了下图右边的状态, cur 是 p 的右孩子,根据 - - 的遍历顺序,下一个节点就是 p。
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 = cur->_parent;parent = parent->_parent;}_node = parent;}return *this;}
反向迭代器
反向迭代器主要是对正向迭代器的封装。
template<class T, class Ref, class Ptr>struct _RBTreeReverseIterator{typedef RBTreeNode<T> Node;typedef _RBTreeReverseIterator<T, Ref, Ptr> self;_RBTreeReverseIterator(Node* node){_it._node = node;}_RBTreeReverseIterator(const _RBTreeIterator<T, T&, T*>& it){_it._node = it._node;}_RBTreeReverseIterator(const _RBTreeReverseIterator<T, T&, T*>& it){_it._node = it._it._node;}self& operator++(){--_it;return *this;}self& operator--(){++_it;return *this;}bool operator!=(const self& s){return _it._node != s._it._node;}Ptr operator->(){return &_it._node->_data;}Ref operator*(){return _it._node->_data;}struct _RBTreeIterator<T,Ref,Ptr> _it=nullptr;};
在红黑树中实现了 迭代器,接下来就是封装到 map、set 里面。与此同时,还需要红黑树中提供 begin() 、end() 、rbegin() 、rend() 方法。
如下,是在红黑树中实现这些方法,返回的结果是迭代器。
template<class K, class T, class KeyOfT>class RBTree{typedef struct RBTreeNode<T> Node;public:typedef _RBTreeIterator<T, T&, T*> iterator;typedef _RBTreeIterator<T, const T&, const T*> const_iterator;typedef _RBTreeReverseIterator<T, T&, T*> reverse_iterator;typedef _RBTreeReverseIterator<T, const T&, const T*> const_reverse_iterator;public: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 const_iterator(cur);}const_iterator end() const{return const_iterator(nullptr);}reverse_iterator rbegin(){Node* cur = _root;while (cur && cur->_right){cur = cur->_right;}return reverse_iterator(cur);}reverse_iterator rend(){return reverse_iterator(nullptr);}const_reverse_iterator rbegin() const{Node* cur = _root;while (cur && cur->_right){cur = cur->_right;}return const_reverse_iterator(cur);}const_reverse_iterator rend() const{return const_reverse_iterator(nullptr);}private:Node* _root;};
如下是在 set 里面实现 begin() 、end() ,完全是对 红黑树方法的复用。
值得注意的是, typedef typename RBTree<K, K, KeyOfT>::const_iterator iterator; 这是为了保证 set 的 K 不可修改。
#pragma once
#include"rb_tree.h"namespace SetSimulate
{template<class K>class set{struct KeyOfT{const K& operator()(const K& k){return k;}};public:typedef typename RBTreeNode<K> Node;typedef typename RBTree<K, K, KeyOfT>::const_iterator iterator;typedef typename RBTree<K, K, KeyOfT>::const_iterator const_iterator;typedef typename RBTree<K, K, KeyOfT>::const_reverse_iterator reverse_iterator;typedef typename RBTree<K, K, KeyOfT>::const_reverse_iterator const_reverse_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();}reverse_iterator rbegin(){return _t.rbegin();}reverse_iterator rend(){return _t.rend();}const_reverse_iterator rbegin()const{return _t.rbegin();}const_reverse_iterator rend()const{return _t.rend();}iterator Find(const K& key){Node* cur = _t.find(key);return iterator(cur);}private:RBTree<K, K, KeyOfT> _t;};
}
map 的 operator[ ]
首先,map 的 [ ] 是兼具插入和查找功能的。map 里面存储的是 pair<K,V> ,假设有一个map< int,int >对象 m,执行 m[10]=1000; —— 如果存储的 pair<K,V> 里面,有 K 类型的数据是 10,那么就将这个键值对的 V 修改成 100;没有则插入 make_pair(10,100);
要完成上面描述的功能,就必须要让 [ ] 的返回值是 V& (V的引用),这样才可以修改 V 类型的数据。 可以通过迭代器来实现!!
如下是 map 里面实现 operator[ ] 的代码,_t 是红黑树实例化出来的对象,其 Insert() 返回值是 pair<iterator,bool> , 很明显,返回值包含两个信息:
- 某个节点构成的迭代器。如果插入成功,那么返回插入节点封装成的迭代器。如果插入失败(已经有了 K 类型对应的数据),那么就相当于查找功能,返回找到的该节点封装成的迭代器。
- 是否插入成功,插入成功返回 true,否则false。
V& operator[](const K& key){pair<iterator, bool> ret = _t.Insert(make_pair(key, V()));return ret.first->second;}
那么就可以对红黑树底层的 Insert() 进行修改,只需要修改函数的返回值类型,以及 return 的时候,构造一个 make_pair<iterator,bool> 类型的数据进行返回即可。
📕 源代码
rb_tree.h
#pragma once
#include<iostream>
#include<cassert>using namespace std;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& val): _left(nullptr), _right(nullptr), _parent(nullptr), _data(val), _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){}_RBTreeIterator(const _RBTreeIterator<T, T&, T*> &it):_node(it._node){}Ref operator*(){return _node->_data;}bool operator!=(const self& s){return this->_node != s._node;}Ptr operator->(){return &_node->_data;}self& operator++(){// 右子树存在,先去右子树的第一个if (_node->_right){Node* subleft=_node->_right;while (subleft->_left){subleft = subleft->_left;}_node = subleft;}else // 右子树不存在,向上找{Node* cur = _node;Node* parent = _node->_parent;while (parent && parent->_right == cur){cur = cur->_parent;parent = parent->_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 = cur->_parent;parent = parent->_parent;}_node = parent;}return *this;}Node* _node;};template<class T, class Ref, class Ptr>struct _RBTreeReverseIterator{typedef RBTreeNode<T> Node;typedef _RBTreeReverseIterator<T, Ref, Ptr> self;_RBTreeReverseIterator(Node* node){_it._node = node;}_RBTreeReverseIterator(const _RBTreeIterator<T, T&, T*>& it){_it._node = it._node;}_RBTreeReverseIterator(const _RBTreeReverseIterator<T, T&, T*>& it){_it._node = it._it._node;}self& operator++(){--_it;return *this;}self& operator--(){++_it;return *this;}bool operator!=(const self& s){return _it._node != s._it._node;}Ptr operator->(){return &_it._node->_data;}Ref operator*(){return _it._node->_data;}struct _RBTreeIterator<T,Ref,Ptr> _it=nullptr;};template<class K, class T, class KeyOfT>class RBTree{typedef struct RBTreeNode<T> Node;public:typedef _RBTreeIterator<T, T&, T*> iterator;typedef _RBTreeIterator<T, const T&, const T*> const_iterator;typedef _RBTreeReverseIterator<T, T&, T*> reverse_iterator;typedef _RBTreeReverseIterator<T, const T&, const T*> const_reverse_iterator;public: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 const_iterator(cur);}const_iterator end() const{return const_iterator(nullptr);}reverse_iterator rbegin(){Node* cur = _root;while (cur && cur->_right){cur = cur->_right;}return reverse_iterator(cur);}reverse_iterator rend(){return reverse_iterator(nullptr);}const_reverse_iterator rbegin() const{Node* cur = _root;while (cur && cur->_right){cur = cur->_right;}return const_reverse_iterator(cur);}const_reverse_iterator rend() const{return const_reverse_iterator(nullptr);}~RBTree(){Delete(_root);_root = nullptr;}Node* find(const K& val){KeyOfT kt;Node* cur = _root;while (cur){if (val > kt(cur->_data)){cur = cur->_right;}else if (val < kt(cur->_data)){cur = cur->_left;}elsereturn cur;}return nullptr;}pair<iterator,bool> Insert(const T& val){if (_root == nullptr){_root = new Node(val);_root->_col = BLACK;return make_pair(iterator(_root),true);}KeyOfT kt;Node* parent = nullptr;Node* cur = _root;while (cur) // 找到要插入的位置{if (kt(cur->_data) > kt(val)){parent = cur;cur = cur->_left;}else if (kt(cur->_data) < kt(val)){parent = cur;cur = cur->_right;}elsereturn make_pair(iterator(cur), false);}// 插入cur = new Node(val);Node* newnode = cur;if (kt(parent->_data) < kt(val)){parent->_right = cur;}else if (kt(parent->_data) > kt(val)){parent->_left = cur;}cur->_parent = parent;// 调整颜色while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED) // uncle 为红色{parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;// 继续调整cur = grandfather;parent = cur->_parent;}else // uncle不存在 or 存在且为黑{if (cur == parent->_left){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;//cur->_col = RED;}else if (cur == parent->_right){RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;parent->_col = RED;}break;}}else // if (parent == grandfather->_right){Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED) // uncle 为红色{parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;// 继续调整cur = grandfather;parent = cur->_parent;}else // uncle不存在 or 存在且为黑{if (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;//cur->_col = RED;}else // cur == parent->_left{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;parent->_col = RED;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return make_pair(iterator(newnode), true);}int Height(){return _Height(_root);}bool isBalance(){if (_root->_col == RED){cout << "根节点的颜色是红色" << endl;return false;}int benchmark = 0;Node* tmp = _root;while (tmp){if (tmp->_col == BLACK)benchmark++;tmp = tmp->_left;}return _isBalance(_root, 0, benchmark);}void Inorder(){_Inorder(_root);}private:void _Inorder(Node* root){if (root == nullptr)return;_Inorder(root->_left);cout << root->_kv.first << " ";_Inorder(root->_right);}bool _isBalance(Node* root, int blackNum, int benchmark){if (root == nullptr){if (blackNum == benchmark)return true;else{cout << "某条链路黑色节点数目错误!" << endl;return false;}}if (root->_col == BLACK)++blackNum;else{if (root->_parent && root->_parent->_col == RED){cout << "存在两个连续的红色节点" << endl;return false;}}return _isBalance(root->_left, blackNum, benchmark)&& _isBalance(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 Delete(Node* root){if (root == nullptr)return;Delete(root->_left);Delete(root->_right);free(root);}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* pparent = parent->_parent;subR->_left = parent;parent->_parent = subR;if (pparent == nullptr){_root = subR;_root->_parent = nullptr;}else{if (parent == pparent->_left){pparent->_left = subR;}else{pparent->_right = subR;}subR->_parent = pparent;}}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;Node* pparent = parent->_parent;subL->_right = parent;parent->_parent = subL;parent->_left = subLR;if (subLR)subLR->_parent = parent;if (pparent == nullptr){subL->_parent = nullptr;_root = subL;}else{if (parent == pparent->_left){pparent->_left = subL;}else{pparent->_right = subL;}subL->_parent = pparent;}}private:Node* _root;};
set.h
#pragma once
#include"rb_tree.h"namespace SetSimulate
{template<class K>class set{struct KeyOfT{const K& operator()(const K& k){return k;}};public:typedef typename RBTreeNode<K> Node;typedef typename RBTree<K, K, KeyOfT>::const_iterator iterator;typedef typename RBTree<K, K, KeyOfT>::const_iterator const_iterator;typedef typename RBTree<K, K, KeyOfT>::const_reverse_iterator reverse_iterator;typedef typename RBTree<K, K, KeyOfT>::const_reverse_iterator const_reverse_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();}reverse_iterator rbegin(){return _t.rbegin();}reverse_iterator rend(){return _t.rend();}const_reverse_iterator rbegin()const{return _t.rbegin();}const_reverse_iterator rend()const{return _t.rend();}pair<iterator,bool> insert(const K& k){return _t.Insert(k);}iterator Find(const K& key){Node* cur = _t.find(key);return iterator(cur);}private:RBTree<K, K, KeyOfT> _t;};
}
map.h
#pragma once
#include"rb_tree.h"namespace MapSimulate
{template<class K, class V>class map{struct MapOfKey{const K& operator()(const pair<const K, V>& kv){return kv.first;}};public:typedef typename RBTreeNode<pair<const K, V>> Node;typedef typename RBTree<K, pair<const K,V>, MapOfKey>::iterator iterator;typedef typename RBTree<K, pair<const K,V>, MapOfKey>::const_iterator const_iterator;typedef typename RBTree<K, pair<const K, V>, MapOfKey>::reverse_iterator reverse_iterator;typedef typename RBTree<K, pair<const K, V>, MapOfKey>::const_reverse_iterator const_reverse_iterator;pair<iterator,bool> insert(const pair<const 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;}iterator begin(){return _t.begin();}iterator end(){return _t.end();}const_iterator begin()const{return _t.begin();}const_iterator end()const{return _t.end();}reverse_iterator rbegin(){return _t.rbegin();}reverse_iterator rend(){return _t.rend();}const_reverse_iterator rbegin()const{return _t.rbegin();}const_reverse_iterator rend()const{return _t.rend();}iterator Find(const K& key){Node* cur = _t.find(key);return iterator(cur);}private:RBTree<K, pair<const K, V>, MapOfKey> _t;};
}