map和set的使用以及模拟实现
2017-11-04 23:52
316 查看
1,set
我们先来看看STL中set的接口有哪些
![](https://img-blog.csdn.net/20171104212022639?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQ2hhc2VSYW9k/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
![](https://img-blog.csdn.net/20171104212036608?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQ2hhc2VSYW9k/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
set的底层使用红黑树来实现,红黑树是一个不暴露给外界的数据结构,map和set都用它来实现,所以map和set是属于关联式容器。
set的特性:
所有的元素都会根据元素的键值自动排序,set的元素不像map那样可以同时拥有实值(value)和键值(key),set元素的键值就是实值,实值就是键值,set不允许两个元素有相同的键值。
我们不可以通过set的迭代器来改变set的元素值,因为其元素值就是键值,关系到set元素的排列规则。因此,在STL中set的迭代器底层是rb_tree的const_iterator.
以下是对set部分接口的使用:
![](https://img-blog.csdn.net/20171104220049766?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQ2hhc2VSYW9k/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
由以上打印结果,我们可以看出,set中的insert使用的是rb_tree的insert_unique(),而不是insert_equal(),因为set不允许相同键值存在,multiset才允许相同键值存在。
2,map
先来看map的接口
![](https://img-blog.csdn.net/20171104220622224?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQ2hhc2VSYW9k/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
![](https://img-blog.csdn.net/20171104220637463?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQ2hhc2VSYW9k/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
font size=4 color=#8B008B>map的特性:
所有元素都会根据元素的键值自动被排序,map的所有元素都是pair,同时拥有实值(value)和键值(key)。pair的第一个元素被视为键值,第二个元素被视为实值。map不允许两个元素拥有相同的键值。
pair的定义如下:
为什么要有pair呢?
因为map是K,V模型,而一个函数不能有两个返回值,要返回两个值需要返回一个结构体,pair就是这个要返回的结构体。
map的键值(key)不能被修改,但实值(value)可以修改.
我们来简单使用以下map的部分接口:
![](https://img-blog.csdn.net/20171104225705690?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQ2hhc2VSYW9k/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
map中的make_pair返回一个pair对象。
我们再来介绍map的operator[]:
首先,根据类型推演产生一个临时对象value_type(k,T())
再将该元素插入到map中
插入操作返回一个pair,其第一个元素是个迭代器,指向插入妥当的新元素,或指向插入失败点(键值重复)的旧元素。
取迭代器中的第二个元素,是个bool值
关于map的一道面试题:
统计公司员工最喜欢吃的前K中水果
下面我们来简单模拟map和set的部分接口的实现。
由于它们底层都是红黑树,所以我们对之前红黑树的代码做部分调整即可。需要清楚的是:set的value_type是K类型,而map的value_type是pair类型。
rb_tree.h
myset.h
![](https://img-blog.csdn.net/20171104235127926?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQ2hhc2VSYW9k/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
mymap.h
![](https://img-blog.csdn.net/20171104235214329?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQ2hhc2VSYW9k/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
multiset和multimap
multiset的特性以及用法和set完全相同,唯一的差别在于它允许键值重复,因此它的插入操作采用的底层机制是RB_Tree的insert_equal,而非insert_unique.
用上述代码对比二者的打印结果即可:
![](https://img-blog.csdn.net/20171105085419814?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQ2hhc2VSYW9k/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
mutilmap的特性和用法和map也是完全相同,唯一的区别就在于它允许键值冗余,因此它的插入的底层机制采用RB_Tree的insert_equal.同样用代码来看效果.
我们先来看看STL中set的接口有哪些
set的底层使用红黑树来实现,红黑树是一个不暴露给外界的数据结构,map和set都用它来实现,所以map和set是属于关联式容器。
set的特性:
所有的元素都会根据元素的键值自动排序,set的元素不像map那样可以同时拥有实值(value)和键值(key),set元素的键值就是实值,实值就是键值,set不允许两个元素有相同的键值。
我们不可以通过set的迭代器来改变set的元素值,因为其元素值就是键值,关系到set元素的排列规则。因此,在STL中set的迭代器底层是rb_tree的const_iterator.
以下是对set部分接口的使用:
#include<set> #include<iostream> using namespace std; void Test() { set<int> s; int a[10] = { 10, 9, 8, 5, 4, 2, 1, 3, 7, 6 }; for (size_t i = 0; i < 10; i++) { s.insert(a[i]); } set<int>::iterator it1 = s.begin(); while (it1 != s.end()) { cout << *it1 << " "; ++it1; } cout << endl; cout << "Size = " << s.size() << endl; cout << "3 Count = " << s.count(3) << endl; s.insert(5); cout << "Size = " << s.size() << endl; s.insert(11); cout << "Size = " << s.size() << endl; set<int>::iterator it2 = s.begin(); while (it2 != s.end()) { cout << *it2 << " "; ++it2; } cout << endl; s.erase(2); set<int>::iterator it3 = s.begin(); while (it3 != s.end()) { cout << *it3<< " "; ++it3; } cout << endl; }
由以上打印结果,我们可以看出,set中的insert使用的是rb_tree的insert_unique(),而不是insert_equal(),因为set不允许相同键值存在,multiset才允许相同键值存在。
2,map
先来看map的接口
font size=4 color=#8B008B>map的特性:
所有元素都会根据元素的键值自动被排序,map的所有元素都是pair,同时拥有实值(value)和键值(key)。pair的第一个元素被视为键值,第二个元素被视为实值。map不允许两个元素拥有相同的键值。
pair的定义如下:
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呢?
因为map是K,V模型,而一个函数不能有两个返回值,要返回两个值需要返回一个结构体,pair就是这个要返回的结构体。
map的键值(key)不能被修改,但实值(value)可以修改.
我们来简单使用以下map的部分接口:
void Test() { map<string, int> p; p[string("liuyulin")] = 1; p[string("zhangyuhao")] = 2; p[string("cuicui")] = 3; p[string("yuxi")] = 4; pair<string, int> value(string("daiwei"), 5); p.insert(value); map<string, int>::iterator it = p.begin(); while (it != p.end()) { cout << it->first << ":" << it->second << endl; ++it; } int num = p[string("liuyulin")]; cout << num << endl; map<string< 4000 /span>, int>::iterator it1; it1 = p.find(string("zhangyuhao")); if (it1 == p.end()) { cout << "zahngyuhao found" << endl; } it1->second = 6;//可以修改second的值 int num2 = p["zhangyuhao"]; cout << num2 << endl; }
map中的make_pair返回一个pair对象。
tempalate<class K,class V> inline pair<k,v>make_pair(const K& key,const V& value) { return pair<k,v>(key,value);//类型推演 }
我们再来介绍map的operator[]:
typedef Key key_value; typedef pair<const Key,T> value_type; T& operator[](const key_type& k) { return (*((insert(value_type(k,T()))).first)).second; }
首先,根据类型推演产生一个临时对象value_type(k,T())
再将该元素插入到map中
insert(value_type(k,T())
插入操作返回一个pair,其第一个元素是个迭代器,指向插入妥当的新元素,或指向插入失败点(键值重复)的旧元素。
(insert(value_type(k,T()))).first
取迭代器中的第二个元素,是个bool值
(*((insert(value_type(k,T()))).first)).second;
关于map的一道面试题:
统计公司员工最喜欢吃的前K中水果
map<string,int>countMap; string str[] = {"苹果","香蕉","橘子","苹果","橘子"}; for(size_t i = 0;i<sizeof(str)/sizof(str[0]);++i) { map<string,int>::iterator it = countMap.find(str[i]); (1)if(it != countMap.end())//说明已经有这种水果了 { it->second++; } else { (2)//countMap.insert(pair<string,int>(str[i],1)); countMap.insert(make_pair(s[i],1);//make_pair是一个模板函数 } (3)//countMap[str[i]]++; }
下面我们来简单模拟map和set的部分接口的实现。
由于它们底层都是红黑树,所以我们对之前红黑树的代码做部分调整即可。需要清楚的是:set的value_type是K类型,而map的value_type是pair类型。
rb_tree.h
#pragma once #include<iostream> using namespace std; enum Colour { RED, BLACK, }; template<class ValueType> struct RBTreeNode { ValueType _valueField; RBTreeNode<ValueType>* _left; RBTreeNode<ValueType>* _right; RBTreeNode<ValueType>* _parent; Colour _col; RBTreeNode(const ValueType& v) :_valueField(v) , _left(NULL) , _right(NULL) , _parent(NULL) , _col(RED) {} }; //map返回pair //set返回K template<class ValueType> struct __TreeIterator { typedef RBTreeNode<ValueType>Node; typedef __TreeIterator<ValueType> Self; Node* _node; __TreeIterator(Node* node) :_node(node) {} ValueType& operator*() { return _node->_valueField; } ValueType* operator->() { return &(_node->_valueField); } bool operator == (const Self& s) { return _node = s._node; } bool operator != (const Self& s) { return _node != s._node; } Self& operator++()//前置++ { if (_node->_right) { Node* SubRight = _node->_right; while (SubRight->_left) { //访问右子树的最左节点 SubRight = SubRight->_left; } _node = SubRight; } else { Node* cur = _node; Node* parent = cur->_parent; while(parent && cur == parent->_right) { cur = parent; parent = cur->_parent; } _node = parent; } return *this; } Self operator++(int)//后置++ { Self tmp(*this); ++tmp; return *this; } Self& operator--() { if (_node->_left) { Node* Subleft = _node->_left; while (Subleft->_right) { //左子树的最右节点 Subleft = Subleft->_right; } _node = Subleft; } else { Node* cur = _node; Node* parent = cur->_parent; while (parent && cur == parent->_left) { cur = parent; parent = cur->_parent; } _node = parent; } return *this; } Self operator--(int) { Self tmp(*this); --tmp; return *this; } }; template<class K,class V,class KeyOfValue> class RBTree { typedef V ValueType; typedef RBTreeNode<ValueType> Node; public: typedef __TreeIterator<ValueType> Iterator; RBTree() :_root(NULL) {} RBTree(const RBTree<K, V,KeyOfValue>& tree) { _Copy(tree._root); } ~RBTree() { _Destroy(_root); } RBTree<K, V,KeyOfValue>& operator = (const RBTree<K, V,KeyOfValue>& tree) { RBTree<K, V,KeyOfValue> tmp(tree); swap(_root, tree._root); return *this; } Iterator Begin() { Node* cur = _root; while (cur && cur->_left) { cur = cur->_left; } return cur;//单参数的构造函数允许隐式类型转换 } Iterator End() { return NULL; } Iterator ReBegibn() { Node* cur = _root; while (cur->_right) { cur = cur->_right; } return cur; } Iterator ReEnd() { return NULL; } pair<Iterator,bool> Insert(const ValueType& v) { if (_root == NULL) { _root = new Node(v); _root->_col = BLACK; return make_pair(Iterator(_root),true); } KeyOfValue keyOfValue; Node* parent = NULL; Node* cur = _root; while (cur) { if (keyOfValue(cur->_valueField) < keyOfValue(v)) { parent = cur; cur = cur->_right; } else if (keyOfValue(cur->_valueField) > keyOfValue(v)) { parent = cur; cur = cur->_left; } else { return make_pair(Iterator(cur),false); } } Node* newNode = cur; cur = new Node(v); if (keyOfValue(parent->_valueField) < keyOfValue(v)) { parent->_right = cur; cur->_parent = parent; } else { 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) { parent->_col = BLACK; uncle->_col = BLACK; grandfather->_col = RED; cur = grandfather; parent = cur->_parent; } else // u 不存在 u黑 { if (cur == parent->_right) // 双旋 { RotateL(parent); swap(cur, parent); } RotateR(grandfather); parent->_col = BLACK; grandfather->_col = RED; break; } } //parent = grandfather->right else if (parent == grandfather->_right) { Node* uncle = grandfather->_left; if (uncle && uncle->_col == RED) { parent->_col = BLACK; uncle->_col = BLACK; grandfather->_col = RED; cur = grandfather; parent = cur->_parent; } else { if (cur == parent->_right) { RotateR(parent); swap(parent, cur); } RotateL(grandfather); parent->_col = BLACK; grandfather->_col = RED; break; } } } _root->_col = BLACK; return make_pair(Iterator(newNode),true); } void InOrder() { _InOrder(_root); } protected: void _Copy(Node* root) { Node* newNode = NULL; Node* cur = root; while (cur) { newNode = new Node(cur->_key, cur->_value); newNode->_left = _Copy(cur->_left); newNode->_right = _Copy(cur->_right); } } void _Destroy(Node* root) { Node* cur = root; if (root == NULL) return; _Destroy(cur->_left); _Destroy(cur->_right); delete cur; cur = NULL; } Iterator Find(const K& key) { KeyOfValue keyOfValue; Node* cur = _root; while (cur) { if (keyOfValue(cur->_valueField) > key) { cur = cur->_left; } else if (keyOfValue(cur->_valueField) < key) { cur = cur->_right; } else { return cur; } } return Iterator(NULL); } //右旋 void RotateR(Node* parent) { 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 (ppNode == NULL) { _root = subL; subL->_parent = NULL; } else { if (ppNode->_left == parent) { ppNode->_left = subL; } else { ppNode->_right = subL; } subL->_parent = ppNode; } } //左旋 void RotateL(Node* parent) { 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 = subR; if (ppNode == NULL) { _root = subR; subR->_parent = NULL; } else { if (ppNode->_left == parent) { ppNode->_left = subR; } else { ppNode->_right = subR; } subR->_parent = ppNode; } } void _InOrder(Node* root) { Node* cur = root; if (cur == NULL) return; _InOrder(cur->_left); cout << cur->_valueField<< " "; _InOrder(cur->_right); } private: Node* _root; };
myset.h
#pragma once #include"rbtree.h" template<class K> class Set { public: struct KeyOfValue { const K& operator()(const K& k) { return k; } }; //typename是告诉编译器在实例化的时候确定类型,延迟确认 typedef typename RBTree<K, K,KeyOfValue>::Iterator Iterator; pair<Iterator, bool> InSert(const K& key) { return _tree.Insert(key); } Iterator Begin() { return _tree.Begin(); } Iterator End() { return _tree.End(); } Iterator ReBegin() { return _tree.ReBegibn(); } Iterator ReEnd() { return _tree.ReEnd(); } void InOrder() { _tree.InOrder(); } private: RBTree<K, K,KeyOfValue> _tree; }; void TestSet() { Set<int> s; s.InSert(10); s.InSert(15); s.InSert(12); s.InSert(11); s.InSert(13); s.InSert(14); s.InSert(16); Set<int>::Iterator it = s.Begin(); while (it != s.End()) { cout << *it << " "; ++it; } cout << endl; }
mymap.h
#pragma once #include"rbtree.h" #include<string> template<class K,class V> class Map { public: typedef pair<K, V> ValueType; struct KeyOfValue { const K& operator()(const ValueType& kv) { return kv.first; } }; typedef typename RBTree<K, ValueType, KeyOfValue>::Iterator Iterator; pair<Iterator, bool>InSert(const ValueType& v) { return _tree.Insert(v); } Iterator Begin() { return _tree.Begin(); } Iterator End() { return _tree.End(); } void InOrder() { _tree.InOrder(); } private: RBTree<K, ValueType,KeyOfValue>_tree; }; void TestMap() { Map<string, string> dict; dict.InSert(make_pair("sort", "排序")); dict.InSert(make_pair("insert", "插入")); dict.InSert(make_pair("left", "左边")); dict.InSert(make_pair("right", "右边")); Map<string, string>::Iterator it = dict.Begin(); while (it != dict.End()) { cout << it->first << ":" << it->second <<endl; ++it; } cout << endl; }
multiset和multimap
multiset的特性以及用法和set完全相同,唯一的差别在于它允许键值重复,因此它的插入操作采用的底层机制是RB_Tree的insert_equal,而非insert_unique.
void TestSet() { int a[] = { 1, 2, 2, 3, 4, 4 }; set<int> s; for (size_t i = 0; i < sizeof(a) / sizeof(a[0]); ++i) { s.insert(a[i]); } set<int>::iterator it1 = s.begin(); cout << "set:"; while (it1 != s.end()) { cout << *it1 << " "; ++it1; } cout << endl; multiset<int> ms; for (size_t i = 0; i < sizeof(a) / sizeof(a[0]); ++i) { ms.insert(a[i]); } multiset<int>::iterator it2 = ms.begin(); cout << "multiset:"; while (it2 != ms.end()) { cout << *it2 << " "; ++it2; } cout << endl; }
用上述代码对比二者的打印结果即可:
mutilmap的特性和用法和map也是完全相同,唯一的区别就在于它允许键值冗余,因此它的插入的底层机制采用RB_Tree的insert_equal.同样用代码来看效果.
相关文章推荐
- idea自动生成get、set方法,以及使用lombok插件实现get、set方法
- Spring学习笔记 关于Bean属性的初始化 - 使用inner bean以及List, Map与Set的初始化
- android 使用BitmapShader实现圆形以及放大镜效果
- List、Set、Map 底层实现和使用推荐
- (11) java集合 ---- LinkedHashMap和HashMap等Map的比较使用以及 LinkedHashMap的实现原理
- Stack和Queue:后进先出和先进先出和Java 实现后进先出(LIFO)及Stac和Java集合类: Set、List、Map、Queue使用场景梳理 - .Little Hann
- list、vector、deque、map以及set类型分别使用的情况
- 集合collection的实现类List,set以及map
- STL中vector的使用以及模拟实现
- hp 使用curl模拟登录discuz以及模拟发帖(含formhash的实现)
- 使用Log4j为项目配置日志输出应用详解以及示例演示的实现分析
- 使用Ajax实现DropDownList和ListBox的联动以及两个ListBox之间数据的移动
- 使用java简单模拟ping和telnet的实现
- 使用Ajax实现DropDownList和ListBox的联动以及两个ListBox之间数据的移动
- 使用java简单模拟ping和telnet的实现
- 使用java简单模拟ping和telnet的实现
- 转载---使用Ajax实现DropDownList和ListBox的联动以及两个ListBox之间数据的移动
- 使用java简单模拟ping和telnet的实现
- Hibernate使用Map实现多对多映射
- 使用Javascript实现简单的Map