目录
- 1. set和map的底层结构
- 1.1 红黑树
- 1.2 set
- 1.3 map
- 2. 模拟实现
- 2.1 红黑树
- 2.1 map和set以及仿函数
- 2.3 迭代器
- 2.3.1 const迭代器
- 2.3 set和map封装
1. set和map的底层结构
1.1 红黑树
这两个容器底层都是对红黑树的封装,因此需要先看一下红黑树结构部分的底层源码:
//树结点的定义:
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<Value> rb_tree_node;
public:typedef Key key_type;typedef Value value_type;typedef rb_tree_node* link_type;
protected:size_type node_count; // keeps track of size of treelink_type header; Compare key_compare;}
可以发现红黑树的结点中存储的元素类型只与第二个模板参数Value有关,对于set而言value是Key,对于map而言value是pair<const K, T>类型。
Key也需要传递,树中有些接口的实现需要用到,比如find等。
1.2 set
set底层结构的部分构成:
template <class Key, class Compare = less<Key>, class Alloc = alloc>
class set {
public:// typedefs:typedef Key key_type;typedef Key value_type;typedef Compare key_compare;typedef Compare value_compare;
private:///红黑树typedef rb_tree<key_type, value_type, identity<value_type>, key_compare, Alloc> rep_type;rep_type t; // red-black tree representing set}
set是K模型,因此key_type是Key,value_type也是Key,实例化后树中要存储的元素类型也是Key类型。
1.3 map
template <class Key, class T, class Compare = less<Key>, class Alloc = alloc>
class map {
public:
// typedefs:typedef Key key_type;typedef T data_type;typedef T mapped_type;typedef pair<const Key, T> value_type;typedef Compare key_compare;private:typedef rb_tree<key_type, value_type, select1st<value_type>, key_compare, Alloc> rep_type;rep_type t; // red-black tree representing map
}
对于map而言,key_type是Key,而value_type则是一个pair<const Key, T>类型,因此实例化后树中的结点存储的就是pair类型。
2. 模拟实现
2.1 红黑树
由于map和set用到的是同一个类模板,因此红黑树类模板的设计要支持泛型。
树结点的定义:
template<class T>
struct RBTreeNode {RBTreeNode(const T& data):_data(data), _left(nullptr), _right(nullptr), _parent(nullptr), _col(RED){ }T _data;enum Color _col;RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;
};
对于set而言,T就是key,而对于map而言T为pair<const K, T>类型。
树的定义:
template<class K, class T, class KeyOfT>
//第三个模板参数为仿函数
//作用是取出date中的key来进行比较
//插入元素时要按照key的关键码进行比较选择合适的插入位置
//但是插入的是data,对于map来说data无法按照预期进行比较
//因为data的类型T为pair,需要取出data中的key
//而对于set而言,可以用data比,因为data的类型T就是key
//不过为了统一处理,都通过仿函数来取出key
class RBTree {typedef RBTreeNode<T> Node;
public:typedef Iterator<T> iterator;
public:pair<iterator, bool> insert(const T& data) {if (!_root) {_root = new Node(data);_root->_col = BLACK;return pair<iterator, bool>(_root, true);}Node* cur = _root;Node* parent = nullptr;//定义仿函数对象KeyOfT kot;while (cur) {//取出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 pair<iterator, bool>(cur, false);}}cur = new Node(data);pair<iterator, bool> ret(cur, true);if (kot(parent->_data) < kot(data)) {parent->_right = cur;}else {parent->_left = cur;}cur->_parent = parent;//parent为红需要一直往上调整while (parent && parent->_col == RED) {Node* grandpa = parent->_parent;if (grandpa->_left == parent) {Node* uncle = grandpa->_right;//叔叔存在且为红if (uncle && uncle->_col == RED) {//把父亲和叔叔染黑//爷爷染红继续往上调整parent->_col = uncle->_col = BLACK;grandpa->_col = RED;cur = grandpa;parent = cur->_parent;}//叔叔不存在或者存在且为黑else {//单纯的一边高(直线)单旋if (cur == parent->_left) {rotateRight(grandpa);parent->_col = BLACK;cur->_col = grandpa->_col = RED;}//折线情况需要双旋else {rotateLeft(parent);rotateRight(grandpa);cur->_col = BLACK;parent->_col = grandpa->_col = RED;}break;}}//同样的逻辑else {Node* uncle = grandpa->_left;if (uncle && uncle->_col == RED) {parent->_col = uncle->_col = BLACK;grandpa->_col = RED;cur = grandpa;parent = cur->_parent;}else {if (parent->_right == cur) {rotateLeft(grandpa);grandpa->_col = cur->_col = RED;parent->_col = BLACK;}else {rotateRight(parent);rotateLeft(grandpa);parent->_col = grandpa->_col = RED;cur->_col = BLACK;}break;} }}_root->_col = BLACK;return ret;}
private:Node* _root = nullptr;
};
2.1 map和set以及仿函数
set:
template<class K>
class set {struct SetKeyOfT {const K& operator()(const K& key) {return key;}};//显式传递仿函数typedef RBTree<K, const K, SetKeyOfT> rbt;
private:rbt t;
};
map:
template<class K, class T>
class map {//内部类struct MapKeyOfT {const K& operator()(const pair<const K, T>& key) {return key.first;}};typedef RBTree<K, pair<const K, T>, MapKeyOfT> rbt;
private:rbt t;
};
仿函数的作用在于统一取出结点中的Key进行比较。
2.3 迭代器
map和set通过迭代器进行遍历时,本质是对这颗树进行中序遍历,得到的结果是有序的,因此迭代器中最重要的功能是++和–的实现逻辑,对于++,本质是中序遍历时的下一个位置,具体可分为两步:
- 右子树不为空时,需要去找到右子树的最小结点也就是最左节点。
- 右子树为空时,说明当前子树遍历完毕,需要去找到一个根结点(可能是整棵树的根也可能是某颗子树的根),该根结点的左子树是我或者是我的某个祖先结点,因为中序遍历的顺序是:左-根-右
self& operator++() {//右子树不为空,去找右树的最左结点if (_it->_right) {Node* leftMin = _it->_right;while (leftMin && leftMin->_left) {leftMin = leftMin->_left;}_it = leftMin;}//为空//去找孩子是父亲的左的那个根结点else {Node* cur = _it;Node* parent = cur->_parent;while (parent && cur == parent->_right) {cur = cur->_parent;parent = parent->_parent;} _it = parent;}return *this;
}
2.3.1 const迭代器
由于set中的元素不允许修改,所以set的普通和const迭代器都是树中的const迭代器:
但是这种设计会出现一个问题:
会导致类型不匹配,这是因为树的insert返回值pair中的迭代器类型是普通迭代器,而set中的迭代器的类型是const类型,普通迭代器无法构造const迭代器,所以会报错,解决方法为:设计一个支持普通迭代器转化为cons迭代器的构造函数。
对于实例化const迭代器类型的模板类而言,该函数是构造函数,而对于实例化普通迭代器类型的模板类而言,该函数是拷贝构造,通过该函数即可解决上述问题。
map的普通和const迭代器的设计则与其它容器是类似的,只是不管是普通还是const迭代器都无法对key进行修改,而value是否能修改则取决于迭代器的具体类型。
迭代器整体实现代码:
template <class T, class Ref, class Ptr>
struct Iterator {typedef Iterator<T, Ref, Ptr> self;typedef Iterator<T, T&, T*> iterator;typedef Iterator<T, const T&, const T*> const_iterator;typedef RBTreeNode<T> Node;Iterator(const iterator& it) :_it(it._it){ }Iterator(Node* it) :_it(it){ }bool operator!=(const self& s) const {return _it != s._it;}bool operator==(const self& s) const {return !(*this != s);}self& operator++() {if (_it->_right) {Node* leftMin = _it->_right;while (leftMin && leftMin->_left) {leftMin = leftMin->_left;}_it = leftMin;}else {Node* cur = _it;Node* parent = cur->_parent;while (parent && cur == parent->_right) {cur = cur->_parent;parent = parent->_parent;}_it = parent;}return *this;}self& operator--() {if (_it->_left) {Node* leftMax = _it->_left;while (leftMax && leftMax->_right) {leftMax = leftMax->_right;}_it = leftMax;}else {Node* cur = _it;Node* parent = cur->_parent;while (parent && cur == parent->_left) {cur = cur->_parent;parent = parent->_parent;}_it = parent;}return *this;}Ref operator*() {return _it->_data;}Ptr operator->() {return &_it->_data;}Node* _it;
};
2.3 set和map封装
set:
#include "RBTree.h"namespace lzh {template<class K>class set {struct SetKeyOfT {const K& operator()(const K& key) {return key;}};typedef RBTree<K, K, SetKeyOfT> rbt;public:typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;public:iterator begin() const {return _t.begin();}iterator end() const {return _t.end();}pair<iterator, bool> insert(const K& data) {pair<typename RBTree<K, K, SetKeyOfT>::iterator, bool> p = _t.insert(data);return pair<iterator, bool>(p.first, p.second);}private:rbt _t;};
};
map:
#include "RBTree.h"namespace lzh {template<class K, class T>class map {struct MapKeyOfT {const K& operator()(const pair<const K, T>& key) {return key.first;}};typedef RBTree<K, pair<const K, T>, MapKeyOfT> rbt;public:typedef typename RBTree<K, pair<const K, T>, MapKeyOfT>::iterator iterator;typedef typename RBTree<K, pair<const K, T>, MapKeyOfT>::const_iterator const_iterator;public:iterator begin() {return t.begin();}iterator end() {return t.end();}pair<iterator, bool> insert(const pair<const K, T>& data) {return t.insert(data);}T& operator[](const K& k) {return insert({ k, T() }).first->second;}private:rbt t;};
};