set&&map
- set/map简介
- set特性
- map特性
- 红黑树的接口
- set的封装
- map的封装
- 总结
set/map简介
我们所熟知的vector、list、deque被称为序列是容器;set和map是c++中的关联式容器,他们都是用来储存数据的。
只不过关联式容器里面存储的不是单个元素,而是一个<key, value>键值对,这种键值对在数据检索时,效率更高。
键值对:用来表示具有一一对应关系的一种结构,该结构中只包含变量key和value,key代表键值,value表示与key对应的信息。比如英汉互译的词典<apple, 苹果> 就是一个键值对。
set特性
- set是按照一定次序存储元素的容器。
- 在set中,存储的是<key, key>这样的键值对,并且每个key必须是唯一的。set中的元素不能在容器中修改。但是可以从容器中,插入或删除他们
- 在内部,set中的元素总是按照一定的顺序排列
- set插入时,只需要插入value即可,不需要构造键值对
- set底层使用红黑树实现
map特性
- map是按照特定顺序存储的容器
- map中存储的是<key, value>这样的键值对。并且每个key必须唯一。但是value值可以修改。也可以从容器中,插入或删除他们
- 在内部,map是按照key的顺序排列的
- map支持下标访问,且非常灵活。
- map插入时,需要插入一个<key, value>键值对
- map底层是用红黑树实现的
红黑树的接口
修改步骤:
- 将类模板修改为<K, T>, T为存入数据
- 增加一个仿函数的模板选项,用于获取数据的key
- 实现迭代器***
- 迭代器的单参构造(可以实现隐式转换)
- Insert函数,返回值的修改
- 迭代器++的逻辑
- 迭代器的typedef也要好好学学
enum class Color //结点颜色{RED,BLACK};//1. 添加了T模板参数template<class T>struct RBTreeNode{//红黑树结点,默认红色RBTreeNode(const T& data):_left(nullptr),_right(nullptr),_parent(nullptr),_data(data),_color(Color::RED){}RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;T _data;Color _color;};//3. 迭代器实现(数据,数据的引用,数据的指针)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){}// *、typedef __RBTreeIterator<T, T&, T*> itertaor; 拷贝构造// **、 typedef __RBTreeIterator<T, const T&, const T*> const_itertaor;// 支持普通迭代器构造const迭代器的构造函数RBTreeIterator(const RBTreeIterator<T, T&, T*>& it){_node = it._node;}Ref operator*(){return _node->_data;}Ptr operator->(){return &(_node->_data);}bool operator!=(const Self& s){return _node != s._node;}//左根右Self& operator++() //左根右{//1.右不为空,下一个就是右子树的最左结点if (_node->_right){Node* subLeft = _node->_right;while (subLeft->_left){subLeft = subLeft->_left;}_node = subLeft;}else{//2.右为空,沿着到根的路径,找孩子是父亲左的那个祖先Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){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{//左为空,找孩子是父亲的右的结点Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_left){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}};// 2. 注意添加了KeyOfT这个仿函数接口,用来把T里面的key给拿出来template<class K, class T, class KeyOfT>class RBTree{private:typedef RBTreeNode<T> Node;public://析构函数~RBTree(){_Destroy(_root);_root = nullptr;}public: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 const_iterator(cur);}const_iterator end() const{return const_iterator(nullptr);}public:Node* Find(const K& key){Node* cur = _root;KeyOfT kot;while (cur){if (kot(cur->_data) < key){cur = cur->_right;}else if(kot(cur->_data) < key){cur = cur->_left;}else{return cur;}}return nullptr;}pair<iterator, bool> Insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_color = Color::BLACK; //根结点就置为黑色return make_pair(iterator(_root), true);}KeyOfT kot; //仿函数实例化//1.找到插入位置Node* cur = _root;Node* parent = nullptr;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);}}//2.找到了直接插入Node* newNode = new Node(data);cur = newNode;if (kot(parent->_data) > kot(data)){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent; //记得互联//3.检查是否满足红黑树,若满足直接退出//不满足,需要调整红黑树while(parent && parent->_color == Color::RED){//爷爷结点一定存在,因为parent为红,爷爷结点一定为黑!Node* grandfather = parent->_parent;if (grandfather->_left == parent){Node* uncle = grandfather->_right;//情况一: cur 为红,p为红,g为黑, u存在且为红if (uncle && uncle->_color == Color::RED) // 只需要叔父爷变色即可{parent->_color = Color::BLACK;grandfather->_color = Color::RED;uncle->_color = Color::BLACK; //子树调整完成//把爷爷当作新插入结点,继续调整cur = grandfather;parent = cur->_parent;}else //2.u存在且为黑 3.u不存在 旋转+变色{//2 g// p u// cif (cur == parent->_left) //父亲爷爷变色{_RotateR(grandfather); //以g为轴点右单旋parent->_color = Color::BLACK;grandfather->_color = Color::RED;}else //cur是p的右孩子{// g// p u// c 叔父爷变色_RotateL(parent);_RotateR(grandfather);grandfather->_color = Color::RED;cur->_color = Color::BLACK;}break;}}else //parent = grandfather->_right{// g// u p// cNode* uncle = grandfather->_left;if (uncle && uncle->_color == Color::RED) //情况1 u存在且为红{uncle->_color = Color::BLACK;parent->_color = Color::BLACK;grandfather->_color = Color::RED;//继续往上调整cur = grandfather;parent = cur->_parent;}else //u存在为黑 u不存在{// g// u p// cif (cur == parent->_right){//左单旋_RotateL(grandfather);parent->_color = Color::BLACK;grandfather->_color = Color::RED;}else{// g// u p// c_RotateR(parent);_RotateL(grandfather);grandfather->_color = Color::RED;cur->_color = Color::BLACK;}break;}}}_root->_color = Color::BLACK;return make_pair(iterator(newNode), true);}private:void _Destroy(Node* root){if (root == nullptr){return;}_Destroy(root->_left);_Destroy(root->_right);delete root;}void _RotateR(Node* parent){//右单旋,儿子上位变父亲,父亲被挤到右边Node* pParent = parent->_parent; //爷爷Node* childL = parent->_left; //左孩子parent->_left = childL->_right;if (childL->_right){childL->_right->_parent = parent; //子指向父}childL->_right = parent;parent->_parent = childL; //子指向父//最后将chldL指向pParentchildL->_parent = pParent;if (pParent == nullptr) // childL 变成了根{_root = childL;childL->_parent = nullptr;}else{if (pParent->_left == parent){pParent->_left = childL;}else{pParent->_right = childL;}}}//左单旋void _RotateL(Node* parent){Node* childR = parent->_right; //存右孩子parent->_right = childR->_left; //右孩子的左孩子互连父亲if (childR->_left){childR->_left->_parent = parent;}childR->_left = parent; //parent和childR互联Node* pparent = parent->_parent; //暂存爷爷parent->_parent = childR;//爷爷和childR互联childR->_parent = pparent;if (pparent == nullptr) //爷爷为空,就是根{_root = childR;childR->_parent = nullptr;}else{if (pparent->_left == parent){pparent->_left = childR;}else{pparent->_right = childR;}}}private:Node* _root=nullptr; //根};
set的封装
注意问题:
- 要写一个仿函数,只有自己知道自己的数据类型
- 因为set不可更改value,所以要使用const迭代器,注意typedef的都是const命名的。
- 封装的成员变量直接是红黑树。
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();}public:pair<iterator, bool> Insert(const K& key){return _t.Insert(key);}private:RBTree<K, K, SetKeyOfT> _t; //定义为红黑树};
map的封装
封装步骤:
- 模板参数不需要加仿函数!!
- 定义仿函数时,注意此参数设置应该具体
- 迭代器只需实现一份即可
- 注意[]的实现注意事项
//template<class K, class T, class KeyOfT> 自己写的,写的不好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;iterator begin(){return _t.begin();}iterator end(){return _t.end();}V& operator[](const K& key){pair<iterator, bool> ret = _t.Insert(make_pair(key, V()));return ret.first->second;}pair<iterator, bool> insert(const pair<const K, V>& kv){return _t.Insert(kv);}private:RBTree<K, pair<const K, V>, MapKeyOfT> _t;};
总结
map和set的封装,重点是封装的思想。多学学这里代码的套路。