键值密钥:解锁 C++ 关联容器的存储潜能
目录
1:关联式容器
- vector、list、deque、forward_list等等,这些容器统称为序列是容器,因为底层是线性序列的数据结构,里面存储的是元素本身.
- 关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key,value>结构的键值对,在数据检索比序列式容器效率更高,例如:set、map、unordered_set、unordered_map等.
- 注意:C++STL当中的stack、queue和priority_queue属于容器适配器,它们默认使用的基础容器分别是deque、vector.
2:树形结构与哈希结构
根据应用场景的不同,C++的STL容器总共实现了两种不同结构的关联式容器:树型结构和哈希结构。
| 关联式容器 | 容器结构 | 底层实现 |
set、map、multiset、multimap | 树型结构 | 平衡搜索树(红黑树) |
unordered_set、unordered_map、unordered_multiset、unordered_multimap | 哈希结构 | 哈希表 |
其中,树型结构容器中的元素是一个有序的序列,而哈希结构容器中的元素是一个无序的序列.
3:键值对
用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息.
4:Set And MultiSet
4.1:set的使用
- set是按照一定次序存储元素的容器
- 在set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。
set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们. - 在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行排序.
- set容器通过key访问单个key元素的速度通常比unordered_set容器慢,但它们允许根据顺序对子集进行直接迭代.
- set在底层是用二叉搜索树(红黑树)实现的.
4.1.1:set的模版参数列表

T:set中存放元素的类型,实际在底层存储<value,value>的键值对.Compare:set中元素默认按照小于来比较.Alloc:set元素空间的管理方式,使用STL提供的空间配置器进行管理.
4.1.1.1:set的构造
#define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std; #include <set> #include <vector> void TestConsetuctor() { //构造空的set set<int> s1; vector<int> v1 = { 1,2,3,4,5,5,6,7,7 }; //迭代器区间构造 set<int> s2(v1.begin(), v1.end()); //拷贝构造 set<int> s3(s2); for(auto & element : s2) cout << element << " "; cout << endl; for (auto& element : s3) cout << element << " "; cout << endl; } int main() { TestConsetuctor(); }
4.1.1.2:set的迭代器

#define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std; #include <set> #include <vector> void TestIterator() { vector<int> v1 = { 1,2,3,4,5,5,6,7,7 }; set<int> s1(v1.begin(), v1.end()); set<int>::iterator it = s1.begin(); while(it != s1.end()) { cout << *it << " "; it++; } cout << endl; //反向迭代器 set<int>::reverse_iterator rit = s1.rbegin(); while (rit != s1.rend()) { cout << *rit << " "; rit++; } cout << endl; } int main() { TestIterator(); }
4.1.1.3:set的容量
#define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std; #include <set> #include <vector> void Test_Capacity() { vector<int> v1 = { 1,8,9,7,5,6,4,3,2 }; set<int> s1(v1.begin(), v1.end()); cout << s1.size() << endl; cout << s1.empty() << endl; } int main() { Test_Capacity(); }
4.1.1.4:set的修改

#define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std; #include <set> #include <vector> void Test_Modification() { set<int> s1; s1.insert(1); s1.insert(2); s1.insert(3); s1.insert(5); s1.insert(6); s1.insert(7); s1.insert(25); for (auto& element : s1) cout << element << " "; cout << endl; //测试find与erase函数 set<int>::iterator Position = s1.find(2); s1.erase(Position); Position = s1.find(3); size_t count = s1.erase(3); cout <<"删除的元素个数:>" << count << endl; for (auto& element : s1) cout << element << " "; cout << endl; cout <<"25的数量:>" << s1.count(25) << endl; } int main() { Test_Modification(); }
4.1.1.5:set的其他操作
#define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std; #include <set> #include <vector> void Test_Operation() { set<int> s1; s1.insert(1); s1.insert(2); s1.insert(3); s1.insert(5); s1.insert(6); s1.insert(7); s1.insert(25); set<int>::iterator it = s1.find(5); for (auto& element : s1) cout << element << " "; cout << endl; if (s1.end() != it) { cout << *it << endl; s1.erase(it); } else cout << "找不到" << endl; for(auto & element : s1) cout << element << " "; cout << endl; for (size_t i = 1; i < 10; i++) { s1.insert(i * 10); } for (auto element : s1) cout << element << " "; cout << endl; //获取的是闭区域 set<int>::iterator itlow = s1.lower_bound(25); //获取的是比70大的值,但是小于80 set<int>::iterator itup = s1.upper_bound(70); //[25,80) s1.erase(itlow, itup); for (auto& element : s1) cout << element << " "; cout << endl; } int main() { Test_Operation(); }
4.2:multiset
- multiset是按照特定顺序存储元素的容器,其中元素是可以重复的
- 在multiset中,元素的value也会识别它(因为multiset本身存储的是<value,value>组成的键值对,因此value本身就是key,key就是value,类型为T).multiset的元素的值不能在容器中进行修改(因为元素总是const的),但可以从容器中插入或者删除.
- 在内部,multiset中的元素总是按照内部比较规则(类型比较)所指示的特定严格弱排序准则进行排序.
- multiset容器通过key访问单个元素的速度通常比unordered_multiset容器慢,但当使用迭代器遍历时会得到一个有序序列.
- multiset底层结构为红黑树.
PS:
- multiset中在底层中存储的是<value,value>的键值对.
- multiset的插入接口中只需要插入即可.
- 与set的区别是,multiset的元素可以重复,set中,value是唯一的.
- multiset中的元素不能被修改.
#define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std; #include <set> #include <vector> void Test_MultiSet() { //key模型搜索,排序 + 不去重 + 允许数据冗余 multiset<int> s1; s1.insert(1); s1.insert(1); s1.insert(3); s1.insert(2); s1.insert(5); s1.insert(7); s1.insert(9); s1.insert(15); s1.insert(35); for (auto& element : s1) cout << element << " "; } int main() { Test_MultiSet(); }
4.3:Map And MultiMap
- map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素.
- 在map中,键值key通常用于排序和唯一地标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起,为其取别名称为pair:typedef pair<const key,T>value_type;
- 在内部,map中的元素总是按照键值key进行比较排序的。
- map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序
对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。 - map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。
- map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树)).
4.3.1:map的使用

key:键值对中key的类型
T:键值对中value的类型
Compare: 比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递).
Alloc:通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的空间配置器.
注意:在使用map时,需要包含头文件.
4.3.1.1:map的迭代器

#define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std; #include <map> #include <vector> void Test_MapIterator() { map<string, string>m1; pair<string, string>Kv1("Begin", "开始"); m1.insert(Kv1); m1.insert(pair<string, string>("End", "结束")); m1.insert(make_pair("Top", "顶部")); //多参数构造函数支持隐式类型转换 m1.insert({ "Bottom","底部" }); map<string, string>::iterator it = m1.begin(); while (it != m1.end()) { cout << (*it).first << ":" << it->second << endl; ++it; } } int main() { Test_MapIterator(); return 0; }
4.3.1.2:map的容量与元素访问

#define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std; #include <map> #include <vector> void The_Pricinple() { map<string, string> dict; dict.insert({ "string","字符串" }); //插入(一般不这么用) dict["right"]; //插入 + 修改 dict["left"] = "左边"; //返回(返回的是Value的引用) cout << dict["string"] << endl; dict["right"] = "右边"; string str; while (cin >> str) { if (dict.count(str)) { cout << "在字典里面" << endl; } else { cout << "不在字典里面" << endl; } } } int main() { The_Pricinple(); return 0; }

注意:在元素访问时,有一个与operator[]类似的操作at()(该函数不常用)函数,都是通过key找到与key对应的value然后返回其引用,不同的是:当key不存在时,operator[]用默认value与key构造键值对然后插入,返回该默认value,at()函数直接抛异常.
4.3.1.3:map中元素的修改

#define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std; #include <map> #include <vector> void Test_Modify() { map<string, string> Dictionary; Dictionary.insert({ "apple","苹果" }); Dictionary.insert(pair<string, string>("banana", "香蕉")); Dictionary.insert({"watermelon", "西瓜"}); Dictionary.insert({ "orange", "橙子" }); Dictionary.insert({ "lemon", "柠檬" }); for (auto& Kv : Dictionary) cout << Kv.first << ":>" << Kv.second << endl; cout << "----------------------------" << endl; Dictionary.erase("lemon"); Dictionary.erase("watermelon"); for (auto& Kv : Dictionary) cout << Kv.first << ":>" << Kv.second << endl; } int main() { Test_Modify(); return 0; }
4.3.2:Map的总结
- map中的元素总是键值对.
- map中的key是唯一的,并且不能修改.
- 默认按照小于的方式对Key进行比较.
- map中的元素如果用迭代器去进行遍历,可以得到一个有序的序列.
- map的底层为红黑树,查找效率比较高.
- 支持[]操作符,operator[]中实际进行插入查找.
4.3.3:Multimap
- Multimap是关联式容器,它按特定的顺序,存储由Key和value映射成的键值对<key,value>,其中多个键值对之间的key是可以重复的.
- 在multimap中,通过按照key排序和唯一地标识元素,而映射的value存储与key关联的内容,key和value的类型可能不同,通过multimap内部的成员类型value_type组合在一起,value_type是组合key和value的键值对:typedef pair<const Key, T> value_type;
- 在内部,multimap的元素总是通过其内部比较对象,按照指定的特定严格弱排序对key进行排序
注意:multimap和map的唯一不同就是:map中的key是唯一的,而multimap中key是可以重复的。
4.3.3.1:Multimap的使用
1.multimap中的key是可以重复的.
2. multimap中的元素默认将key按照小于来比较.
3. multimap中没有重载operator[]操作.
4. 使用时与map包含的头文件相同.
#define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std; #include <map> #include <vector> void Test_MultiMap() { string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜","苹果", "香蕉", "苹果", "香蕉","苹果","草莓", "苹果","草莓" }; multimap<int, string> SortMap; map<string, int> CountMap; //插入 for (auto& element : arr) CountMap[element]++; //插入 for (auto& kv : CountMap) SortMap.insert({ kv.second,kv.first }); //默认按照key值排序 for (auto& kv : SortMap) cout << kv.first << ":" << kv.second << endl; cout << endl; } int main() { Test_MultiMap(); return 0; }