目录
关联容器
关联容器和顺序容器有着根本的不同:关联容器中的元素是按关键字来保存和访问的,而顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的。
关联容器支持高效的关键字查找和访问。
两个主要的关联容器(associative-container),set和map。
set 中每个元素只包含一个关键字。set 支持高效的关键字查询操作一一检查一个给定关键字是否在 set 中。
map 的元素是关键字-值 (key-value)对(也称键-值对)。其中关键字起到索引的作用,值则表示与索引相关联的数据。字典是一个很好的使用 map 的例子:可以将单词作为关键字,将单词释义作为值。
标准库针对set和map一共提供8种不同的关联容器。1.是否运行关键字重复;2.是否按顺序保存元素
set和multiset定义在头文件set中;map和multimap定义在头文件map中;无序容器则定义在头文件unordered set和unordered map中。
map和multimap映射
map和 multimap映射 将 key - value数对(也称键值对)当作元素进行管理,它们可根据 key 的排序准则自动为元素排序。multimap 允许key重复,map 不允许key重复,对于value不考虑 。如下图所示。
使用map和multimap必须引用头文件map
#include <map>
具体定义如下:
namespace std {
template <typename Key,typename T,
typename Compare =less<Key>,
typename Allocator = allocator<pair<const Key,T> >>
class map;
template <typename Key,typename T,
typename Compare =less<Key>,
typename Allocator =allocator<pair<const Key,T> > >
class multimap;
}
第一个 template 实参将成为元素的 key 类型,第二个 template 实参将成为元素的 value 类型。第三个参数用来说明排序规则,默认为升序。这个排序由key决定,和value无关。 和set类似,map和multimap内部也是由平衡二叉树(红黑树)管理。
map和multimap 会根据元素的 key 自动对元素排序。这么一来,根据已知的 key 查找某个元素时就能够有很好的效率O(logn),而根据已知 value 查找元素时,效率就很糟糕 O(n)。
定义及初始化
定义和初始化如下:
#include <iostream>
#include <map>
#include <string>
using namespace std;
//输出 m的所有元素
void Show(const map<int, string>& m)
{
for (auto i : m)
cout << "(" << i.first << ":" << i.second << ") ";
cout << endl;
}
//输出 m的所有元素
void Show(const multimap<int, string>& m)
{
for (auto i : m)
cout << "(" << i.first << ":" << i.second << ") ";
cout << endl;
}
int main()
{
map<int, string> m1;//创建一个key为int,value为string的空map
map<int, string> m2{ {1, "tcq"}, { 2,"hello" }, {3,"tcq"} };//创建一个带3个元素的map
map<int, string> m3 = m2;//利用m2创建一个新的map对象
map<int, string> m4{ {1, "tcq"}, { 1,"hello" } };//第二个元素的key和第一个重复,不能添加
cout << "m1:"; Show(m1);
cout << "m2:"; Show(m2);
cout << "m3:"; Show(m3);
cout << "m4:"; Show(m4);
multimap<int, string> m5{ {1,"tcq"},{1,"hello" } };//第二个元素的key和第一个重复,能添加
cout << "m5是multimap:"; Show(m5);
return 0;
}
map和multimap的唯一区别是:map不允许key重复而multimap允许key重复。(不关注value的情况) 默认情况key是升序排列,如果想改变排序规则也是可以的,如下程序key按降序排列。
#include <iostream>
#include <map>
#include <string>
using namespace std;
//输出 m的所有元素
void Show(const map<int, string>& m)
{
for (auto i : m)
cout << "(" << i.first << ":" << i.second << ") ";
cout << endl;
}
//输出 m的所有元素
void Show(const multimap<int, string>& m)
{
for (auto i : m)
cout << "(" << i.first << ":" << i.second << ") ";
cout << endl;
}
int main()
{
map<int, string, greater<int>> m;//创建一个key为int,value为string,降序排列的空map
m.insert({ 3,"my" });
m.insert({ 1,"tcq" });
m.insert({ 4,"hello" });
m.insert({ 2,"world" });
//输出m的所有内容
for (auto i : m)
cout << "(" << i.first << ":" << i.second << ") ";
return 0;
}
特性
map 映射
- 键唯一性:map中的键具有唯一性,不允许出现重复的键。当尝试插入一个已经存在的键时,新值会覆盖旧值。这一特性在确保数据一致性和避免重复数据方面具有重要意义。例如,在一个用户账号管理系统中,用户名作为键必须唯一,使用map可以方便地管理用户信息,防止出现重复用户名。
- 自动排序:map会根据键的比较规则(默认为小于运算符<)自动对键进行排序。这使得在遍历map时,元素会按照键的升序依次输出。例如,在一个存储单词及其出现次数的map中,插入单词 “apple”、“banana”、“cherry” 后,遍历map会按照字典序依次输出这些单词及其对应的出现次数。
multimap 映射
- 键可重复:multimap的核心特性是允许同一个键对应多个值。在插入元素时,即使键已经存在,也不会覆盖旧值,而是新增一个键值对。例如,在一个存储员工技能的系统中,一个员工(键)可能具备多种技能(值),使用multimap可以准确地记录这种一对多的关系。
- 自动排序:与map一样,multimap会根据键的比较规则(默认为小于运算符<)自动对键进行排序。这保证了在遍历multimap时,键值对会按照键的升序依次输出,方便对数据进行有序处理。
常用迭代器
map和multimap支持双向迭代器,不支持随机迭代器,可以往前和往后,但不能+1,-1(这是随机迭代器)等。 常用的迭代器如下:
#include <iostream>
#include <map>
#include <string>
using namespace std;
//输出 m的所有元素
void Show(const map<int, string>& m)
{
for (auto i : m)
cout << "(" << i.first << ":" << i.second << ") ";
cout << endl;
}
//输出 m的所有元素
void Show(const multimap<int, string>& m)
{
for (auto i : m)
cout << "(" << i.first << ":" << i.second << ") ";
cout << endl;
}
int main()
{
map<int, int>m{ {3, 30}, { 2,20 }, { 1,10 }, {4,40} };
//从前往后输出m的元素
for (auto it = m.begin(); it != m.end(); it++)
cout << "(" << it->first << "," << it->second << ") ";
cout << endl;
//从后往前输出m的元素
for (auto it = m.rbegin(); it != m.rend(); it++)
cout << "(" << it->first << "," << it->second << ") ";
return 0;
}
常用运算符
[ ]是map中最重要和常用的运算符,它可以通过key访问元素。如果这个key存在则返回元素的引用,如果key不存在则添加新元素。但multimap不支持[ ]。
#include <iostream>
#include <map>
#include <string>
using namespace std;
//输出 m的所有元素
void Show(const map<int, string>& m)
{
for (auto i : m)
cout << "(" << i.first << ":" << i.second << ") ";
cout << endl;
}
int main()
{
map<int, string> m;//创建一个包含key为int,value为string的map
m.insert({ 4,"tcq" });
m[3] = "hello";//注意不是3下标存放"hello",而是插入{3,"hello"}
m[4] = "ttt";//把{4,"tcq"}改为{4,"ttt"}
m[100] = "中国";//插入{100,"中国"}元素
Show(m);
return 0;
}
m[key],如果这个key存在则返回元素的引用,如果key不存在,则插入一个新元素。
注意m[key]中的key是关键字,可以是任意类型,不是下标。map和multimap没有下标访问。
#include <iostream>
#include <map>
#include <string>
using namespace std;
//输出 m的所有元素
void Show(const map<int, string>& m)
{
for (auto i : m)
cout << "(" << i.first << ":" << i.second << ") ";
cout << endl;
}
int main()
{
//国家简称到全称的关联数组
map<string, string>m{ {"中国", "中华人民共和国"}, {"美国","美利坚合众国"} };
m["俄罗斯"] = "俄罗斯联邦";//添加一个新元素
for (auto it = m.begin(); it != m.end(); it++) //输出所有元素
cout << "(" << it->first << "," << it->second << ") ";
cout << endl;
m["美国"] = "The United States of America"; //修改元素值
cout << m["美国"];
return 0;
}
multimap不支持[ ]。
除了[ ]外,map和multimap还支持如下运算符
常用成员函数
下面列举map和multimap对象常用的成员函数
应用场景
1. 字典与词汇表管理
map在实现字典或词汇表功能时非常实用。可以将单词作为键,单词的定义或其他相关信息作为值进行存储。例如,在一个简单的英语学习软件中,使用map可以方便地实现单词查询功能,用户输入单词,程序通过map快速查找并返回对应的释义。
2. 统计与计数
map和multimap在统计数据出现次数方面都有广泛应用。map适用于统计唯一元素的出现次数,而multimap更适合统计可能重复出现的元素的次数。例如,在一个文本分析程序中,要统计每个单词在文本中出现的次数,可以使用map。如果需要统计每个字符在文本中出现的所有位置(一个字符可能在多个位置出现),则multimap更为合适。
3. 关系映射
在处理各种实体之间的关系时,map和multimap发挥着重要作用。map用于一对一关系的映射,如员工 ID 与员工姓名的映射;multimap用于一对多关系的映射,如课程与选修该课程的学生的映射。
4. 缓存与查找表
map常被用作缓存机制和查找表。在缓存系统中,将缓存的键(如网页 URL)与缓存的值(如网页内容)存储在map中。当需要访问某个资源时,先在map中查找,如果找到则直接返回缓存的值,提高系统性能。
总结
map和multimap作为强大的关联容器,为数据的组织与处理带来了极大的便利。map以其键的唯一性和自动排序特性,在需要确保数据唯一性及有序检索的场景中表现卓越,如字典构建、唯一元素统计等。而multimap允许键重复的特性,使其在处理一对多关系数据时无可替代,像课程与学生关系管理、字符位置统计等场景中发挥关键作用。充分发挥这两种数据结构的优势,解决更多复杂的实际问题。