C++ 标准模板库(STL)_有序关联容器—— map + multimap(侯捷老师)
STL—— map + multimap
map + multimap
1、map + multimap定义
注意map的key的属性是const
1.1、 map定义
#include<map>
- map的每一个元素都拥有两个值,一个键值(key)和一个实值(value)。可理解为“{键,值}对”。例如,学生的“学号”与“姓名”就可以用map进行描述。
- 它的内部实现是用一个pair来保存这个两个值。所以,map的每一个元素又是一个pair。
(与set相同,map同样是以红黑树RB_Tree为底层机制的关联式容器,所以具有和Set类似的特征,这里不赘述)
1.1.1、 map特征
- 1、每个元素的key值都唯一,Key - value的一 一对应
- 2、元素的值自动进行排序(key用于rb_tree排序),默认比较键的函数是 less(),pair<key, value>才是rb_tree的节点
- 插入、删除、查找 O(log2n)(面试常问)
- 3、元素的key值不能直接被改变(key为const类型),value可以被改变
typedef std::pair<const _Key, _Tp> value_type;
1.2、 multimap定义
multimap 是关联容器的一种,multimap 的每个元素都分为关键字和值两部分,容器中的元素是按关键字排序的,并且允许有多个元素的关键字相同。注意,不能直接修改 multimap 容器中的关键字。
- map/mulitmap不一样的是,map插入的时候调用的是insert_unique(),而multimap调用的是insert_equal()。
1.3、 map结构部分源码
template <class Key, class T, class Compare = less<Key>, class Alloc class map { public: typedef Key key_type;//用于re_tree排序 typedef T data_type; typedef T mapped_type; typedef pair<const Key, T> value_type; // 在rb_tree中value的类型是pair,rb_tree节点 typedef Compare key_compare; private: // select1st直接return T.first 用于rb_tree取到key进行比较大小 typedef rb_tree<key_type, value_type, select1st<value_type>, key_compare, Alloc> rep_type; rep_type t; // map的成员变量,red-black tree representing map // ... // 接口只是对rb_tree的封装 同set iterator begin() { return t.begin(); } iterator end() { return t.end(); } pair<iterator,bool> insert(const value_type& x) { return t.insert_unique(x); } public: //再对类型重命名 typedef typename rep_type::pointer pointer; typedef typename rep_type::const_pointer const_pointer; typedef typename rep_type::reference reference; typedef typename rep_type::const_reference const_reference; typedef typename rep_type::iterator iterator; typedef typename rep_type::const_iterator const_iterator; typedef typename rep_type::reverse_iterator reverse_iterator; typedef typename rep_type::const_reverse_iterator const_reverse_iterator; typedef typename rep_type::size_type size_type; typedef typename rep_type::difference_type difference_type; // ... }
上述默认的仿函数为
_Select1st
template<typename _Pair> struct _Select1st : public unary_function<_Pair, typename _Pair::first_type> { typename _Pair::first_type& operator()(_Pair& __x) const { return __x.first; } };//operator()
节点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) {} #ifdef __STL_MEMBER_TEMPLATES template <class U1, class U2> //pair的first是一个迭代器 pair(const pair<U1, U2>& p) : first(p.first), second(p.second) {} #endif };
1.3、 multimap结构部分源码
同map一样multimap不允许修改key。但是插入使用的是_M_insert_equal
template <class Key, class T, class Compare = less<Key>, class Alloc = alloc> class multimap { public: //下面的定义与map相同 typedef _Key key_type; typedef _Tp data_type; typedef _Tp mapped_type; typedef pair<const _Key, _Tp> value_type; typedef _Compare key_compare; //嵌套类,提供键值key比较函数接口 //继承自<stl_function.h>中的binary_function /* template<class _Arg1, class _Arg2, class _Result> structbinary_function { typedef_Arg1 first_argument_type; typedef_Arg2 second_argument_type; typedef_Result result_type; }; */ private: //底层机制是RB-Tree typedef _Rb_tree<key_type, value_type, _Select1st<value_type>,key_compare, _Alloc> _Rep_type; _Rep_type _M_t; // red-black treerepresenting multimap public: typedef typename _Rep_type::pointer pointer; typedef typename _Rep_type::const_pointer const_pointer; typedef typename _Rep_type::reference reference; typedef typename _Rep_type::const_reference const_reference; //map的迭代器不直接定义为const_iterator,而是分别定义iterator,const_iterator //是因为map的键值key不能被修改,因为必须遵守比较函数的排序规则,所以必须定义为const_iterator //而map的实值value可以被修改,则定义为iterator typedef typename _Rep_type::iterator iterator; typedef typename _Rep_type::const_iterator const_iterator; typedef typename _Rep_type::reverse_iterator reverse_iterator; typedef typename _Rep_type::const_reverse_iteratorconst_reverse_iterator; typedef typename _Rep_type::size_type size_type; typedef typename _Rep_type::difference_type difference_type; typedef typename _Rep_type::allocator_type allocator_type; // allocation/deallocation // 注意:multimap只能使用RB-tree的insert-equal(),不能使用insert-unique() class value_compare : public binary_function<value_type, value_type,bool> { friend class multimap<_Key,_Tp,_Compare,_Alloc>; protected: _Compare comp; value_compare(_Compare __c) : comp(__c) {} public: bool operator()(const value_type& __x, const value_type& __y)const { return comp(__x.first, __y.first); } };
1.4、 map[]操作符(set、multimap没有)
作用:
- 1.读功能。它的参数是一个key值,返回值是一个T的引用。T就是实值(value)的类型。我们利用它的返回值可以读到key值为x的元素的value。
- 2.写功能。写入一个新元素
//map的[]操作符返回传递给map的第二个参数。传递给[]一个key,如果查找到,就是value,否则就是默认值0。 mapped_type& operator[](const key_type& __k) { iterator __i = lower_bound(__k);// 找到__k第一个。 // __i->first is greater than or equivalent to __k. if (__i == end() || key_comp()(__k, (*__i).first)) #if __cplusplus >= 201103L __i = _M_t._M_emplace_hint_unique(__i, std::piecewise_construct, std::tuple<const key_type&>(__k), std::tuple<>()); #else __i = insert(__i, value_type(__k, mapped_type()));//重点在这 #endif return (*__i).second; // 返回key的value,此value为传递进map的第二个参数。 }
先搞清楚下面几点。
- ①巧妙的点就在于它的源码实现,它在里面调用了一个insert。它将insert的返回值得first解引用,并返回解引用后的到值得second。
- ②从上面我们知道insert的返回值是一个pair。pair的first是一个迭代器。当插入成功时,迭代器指向新插入元素。当插入失败时,迭代器指向已存在元素位置。
- ③map的元素是pair类型。pair的second就是value。
分析:
- ①operator[]的参数是一个key值。它将这个key值做为insert的参数,调用insert函数。并得到一个返回值。如果参数key不存在于map中,insert就会插入成功,返回一个pair<Iterator,bool>,这个pair的迭代器就是指向新插入元素的位置。
- ②再对这个返回值是一个pair<Iterator,bool>类型的值
pair<iterator,bool> insert(const value_type& x) { return t.insert_unique(x); } /*返回值为一个pair。pair的first是一个迭代器,second是一个bool值。当插入成功时,返回值pair的迭代器指向插入元素的位置,bool值为true。插入失败时(说明该元素已经存在),迭代器指向已存在元素的位置,bool值为false。*/
这个pair的first是一个迭代器,注意这个迭代器的指向(细看insert第一种实现),对这个迭代器解引用又得到一个pair<key,value>类型的值。并返回它的second(一个value值)。
所以说,当key在map中存在时,operator完成的是读操作。当key不存在是,完成一个写操作。写入一个新的pair<key,T()>。
1.4、 map常见操作
1.4.1、定义和赋值
//定义,typename1, typename2代表不同的数据类型。 map<typename1, typename2> mp; //插入和赋值 1、mp.insert(map<typename1, typename2>::value_type(keys,value)); 2、mp.insert(pair<typename1, typename2>(keys,value)); 3、 mp[1] = value;
注意:
- 1、如果map中存在相同的key,使用insert插入方法不能改变关键字对应的值,插入无效。
- 2、使用数组的赋值方式就可以覆盖原来关键字对应的值
map.insert(map<typename1, typename2>::value_type (key1,value1)); map.insert(map<typename1, typename2>::value_type (key1,value2));//key相同,此次执行无效
#include<iostream> #include<string> #include<vector> #include<map>using namespace std; int main() { map<int, int> m;//定义map m[1] = 1; m[1] = 2;//第3种插入方式 使用下标插入 //key不存在时创建 存在时对原有覆盖 pair<int, int> p1;//定义键值对 p1 = make_pair(2, 7); pair<int, int> p2(2, 4);//直接构造键值对 m.insert(p1); m.insert(p2);//第2种插入方式 使用insert接口 //key不存在时创建 存在时放弃,这句不起作用 m.insert(map<int, int>::value_type(3,3));//第1种插入方式 m.insert(pair<int, int>(4, 4)); cout << m[2]<<endl; map<int, int>::iterator itr; for (itr = m.begin(); itr != m.end(); itr++) { //遍历 cout << itr->first << " " << itr->second << endl; } return 0; }
1.4.2、访问和查找
访问:
- 1、通过下标访问
- 2、通过迭代器访问 正向迭代器
- 反向迭代器
map<typename1, typename2>::iterator itr;//正向迭代器 map<typename1, typename2>::reverse_iterator itr;//正向迭代器
查找1:
- 1、使用
map.count(key)
,查找某个关键字是否在map中出现,返回1(存在)或者0(不存在) - 2、使用
map.find(key)
,返回一个迭代器,如果存在,迭代器指向数据所在位置,如果不存在,迭代器为end位置的迭代器
查找2:
map.lower_bound(key)
返回map中第一个大于或者等于key的迭代器map.upper_bound(key)
返回map中第一个大于key的迭代器map.equal_range(key)
返回一个pair,pair中的第一个变量是lower_bound()返回的迭代器,- 第二个变量是upper_bound()返回的迭代器。
-如果这两个迭代器相等的话,则说明map中不出现这个关键字
//map的每一对映射都有两个typename,这决定了必须能通过一个 itr 来同时访问键和值。 //map 可以使用 itr->first 来访问键, itr->second 来访问值。 #include<iostream> #include<string> #include<map>using namespace std; int main() { map<char, int> mp; mp['m'] = 20; mp['r'] = 30; mp['a'] = 40; map<char,int>::iterator itr; for (itr = mp.begin(); itr != mp.end(); itr++) { cout << itr->first << " " << itr->second << endl; } //反向遍历 map<char, int>::reverse_iterator itr1; for (itr1 = mp.rbegin(); itr1 != mp.rend(); itr1++) { cout << itr1->first << " " << itr1->second << endl; } cout << mp.count('a') << endl;//1 cout << mp.count('b') << endl;//0 map<char, int>::iterator itr2; itr2 = mp.find('m'); if (itr2 != mp.end()) cout << "Find,the value is : " << itr2->second << endl; else cout << "NOT FOUND!" << endl; map<int, string> mapStudent; mapStudent.insert(map<int, string>::value_type(1, "student_one")); mapStudent.insert(map<int, string>::value_type(3, "student_three")); mapStudent.insert(map<int, string>::value_type(5, "student_five")); map<int, string>::iterator iter; //返回第一个key大于或等于2的迭代器,所以返回的迭代器指向key=3的位置 iter = mapStudent.lower_bound(2); cout << iter->second << endl;//student_three //返回第一个key大于2的迭代器,所以返回的迭代器指向key=3的位置 iter = mapStudent.upper_bound(2); cout << iter->second << endl;//student_three //返回第一个key大于或等于3的迭代器,所以返回的迭代器指向key=3的位置 iter = mapStudent.lower_bound(3); cout << iter->second << endl;//student_three //返回第一个key大于3的迭代器,所以返回的迭代器指向key=5的位置 iter = mapStudent.upper_bound(3); cout << iter->second << endl;//student_five // pair<map<int, string>::iterator, map<int, string>::iterator > mappair; mappair = mapStudent.equal_range(2); if (mappair.first == mappair.second) cout << "NOT FOUND" << endl; else cout << "Find" << endl; mappair = mapStudent.equal_range(3); if (mappair.first == mappair.second) cout << "NOT FOUND" << endl; else cout << "Find" << endl; return 0; } }
1.4.3、删除
1、 erase(iterator it); //删除 it 迭代器指向位置的元素 2、erase(iterator first,iterator last); //删除 first~last 范围的元素 3、erase(const Key& key); //通过key关键字删除 4、clear()相当于erase(enumMap.begin(),enumMap.end())
#include<map>#include<string> #include<iostream> using namespace std; int main() { map<int, string> mapStudent; mapStudent.insert(map<int, string>::value_type(1, "student_one")); mapStudent.insert(map<int, string>::value_type(2, "student_two")); mapStudent.insert(map<int, string>::value_type(3, "student_three")); //用迭代器删除1 map<int, string>::iterator iter; iter = mapStudent.find(1); mapStudent.erase(iter); //用关键字删除1,返回0/1 size_t n = mapStudent.erase(2); cout << n << endl;//1 size_t m = mapStudent.erase(1); cout << m << endl;//0,因为前面已经删除key = 1的位置了 //只能下key = 3 for (iter = mapStudent.begin(); iter != mapStudent.end(); iter++) cout << iter->first << " " << iter->second << endl; mapStudent.clear();//清空 //没有输出 for (iter = mapStudent.begin(); iter != mapStudent.end(); iter++) cout << iter->first << " " << iter->second << endl; mapStudent.insert(map<int, string>::value_type(1, "student_one")); mapStudent.insert(map<int, string>::value_type(2, "student_two")); mapStudent.insert(map<int, string>::value_type(3, "student_three")); mapStudent.erase(mapStudent.begin(), mapStudent.end()); //没有输出 for (iter = mapStudent.begin(); iter != mapStudent.end(); iter++) cout << iter->first << " " << iter->second << endl; system("pause"); }
1.4.4、基本操作函数
1、 begin() 返回指向map头部的迭代器
2、end() 返回指向map末尾的迭代器
3、size() 返回map中元素的个数
4、empty() 如果map为空则返回true
5、rbegin() 返回一个指向map尾部的逆向迭代器
6、rend() 返回一个指向map头部的逆向迭代器
参考
1、https://www.geek-share.com/detail/2690215067.html
2、https://blog.csdn.net/Li_haiyu/article/details/84974513
3、https://www.cnblogs.com/fnlingnzb-learner/p/5833051.html
- C++之STL(6)之 map 与 multimap 关联容器
- C++ 标准模板库(STL)——迭代器、迭代器类型以及序列式,关联容器迭代器失效问题(iterator)
- C++ STL中涉及到关联容器部分有关multimap的内容
- C++STL之multimap多重映照容器
- C++(标准库):20---STL容器之(无序容器unordered_set、unordered_multiset、unordered_map、unordered_multimap)
- C++之标准模板库STL续(容器,算法)
- 标准模板库(STL)学习探究之Multimap容器
- C++关联容器之 multimap 关联容器
- 标准模板库(STL)学习探究之Multimap容器
- C++(标准库):19---STL容器之(关联式容器map、multimap)
- C++ 标准模板库STL multimap 使用方法与应用介绍
- C++ 标准模板库(STL)编程示例 - multimap
- C++中STL容器之映射——map/multimap
- STL之关联容器(set /map /multiset /multimap)
- 【C++的探索路20】标准模板库STL之STL的基本概念与容器
- STL 笔记(二) 关联容器 map、set、multimap 和 multimap
- C++ STL multimap容器的使用
- STL概览-关联容器set,multiset,map,multimap(四)
- STL关联容器MAP、SET与multimap、multiset与hashmap、hashset的区别
- STL 笔记(二) 关联容器 map、set、multimap 和 multimap