在掌握map容器的基础下,我们开始multimap容器的学习。为什么要求有map的基础呢,原因在于multimap与map容器十分“相似”,不同点在于map只允许存在唯一键,而multimap能够存储重复键,在理解map的基础上对比归纳这两个容器,能够对关联式容器有个宏观的视角,提高对容器的掌控力。如果读者没有map容器的基础,可跳转《初识模板类pair》开始map容器的学习。
由于multimap允许存在相同键,在对比map的学习下读者有什么感想?要知道,map容器由于键的唯一性,能够通过'[]'、at()访问元素,但是对于multimap容器来说,重复键值意味着一个键对应多个值,不能形成一一对应的关系,所以multimap没有'[]'和at()。其他成员函数与map几乎相差无几,值得一提的是,equal_range()在multimap里能够大放异彩。接下来,我们就开始认识multimap容器吧:
创建multimap与map没有差别,这里代码简单演示一下:
#include<bits/stdc++.h>//万能头,以效率为中心
#include<string>
#include<map>//包含头文件,养成好习惯
/*multimap初始化 */
using namespace std;
void test()
{
multimap<int,int> mmp1;//直接创建
multimap<int,int> mmp2{{1,1,},{2,2},{3,3},{4,4},{5,5}} ;//初始化列表
multimap<int,int> mmp3(mmp2) ;//拷贝构造
multimap<int,int> mmp4;
mmp4=mmp2;//'='运算符重载
}
int main(){
test();
return 0;
}multimap也是能通过仿函数更改默认排序规则的,关于仿函数知识这里就不细说了,将multimap容器改为降序排序的代码如下:
#include<bits/stdc++.h>//万能头,以效率为中心
#include<string>
#include<map>//包含头文件,养成好习惯
/*更改multimap自动排序规则(multimap默认是升序排序) */
struct mycmp//创建公共类
{
bool operator()(int a,int b) const //重载函数调用运算符
{
return a>b;//自定义比较规则
}
};
using namespace std;
void test()
{
multimap<int ,string> mmp1{{3,"Dotcpp"},{1,"编程问答"},{9,"计算机二级C语言"},{0,"数据结构教程"},{7,"Java教程"}};//建议这样初始化 ,直观具体
/*multimap会进行升序排序 */
cout << "multimap会默认进行升序排序:";
for(multimap<int,string>::iterator it = mmp1.begin();it!=mmp1.end();++it) //迭代器遍历
{
cout << it->second << " ";
}
cout << '\n';
/*在容器创建之初就指定排序规则*/
multimap<int ,string,mycmp> mmp2{{3,"Dotcpp"},{1,"编程问答"},{9,"计算机二级C语言"},{0,"数据结构教程"},{7,"Java教程"}};//第三个参数传入仿函数类型
/*multimap按照仿函数降序排序 */
cout << "通过仿函数对multimap进行降序排序:";
for(multimap<int,string>::iterator it = mmp2.begin();it!=mmp2.end();++it) //迭代器遍历
{
cout << it->second << " ";
}
cout << '\n';
}
int main(){
test();
return 0;
}编译结果如下:

multimap容器的成员函数相比map容器没有at(),同时也未能重载'[]'所以不能使用'[]'。下面我们通过表格的形式展示multimap的常用成员函数:
| 函数名 | 参数 | 功能 |
|---|---|---|
insert |
或 | 插入一个键值对。可以指定插入位置提示,但multimap会自动排序。 |
erase |
或 | 删除与键k匹配的所有元素,或删除指定位置的单个元素。 |
find | (const key_type& k) | 查找第一个具有键k的元素,返回指向它的迭代器。如果未找到,返回end()。 |
count | (const key_type& k) | 返回具有特定键k的元素数量。 |
lower_bound | (const key_type& k) | 返回指向第一个键不小于 k 的元素的迭代器。 |
upper_bound | (const key_type& k) | 返回指向第一个键大于 k 的元素的迭代器。 |
equal_range | (const key_type& k) | 返回一个迭代器对(pair<iterator, iterator>),表示具有指定键k的元素范围。 |
clear | 无 | 移除所有元素,清空容器。 |
size | 返回容器中元素的数量。 | |
empty | 检查容器是否为空。 | |
begin | 返回指向容器中第一个元素的迭代器。 | |
end | 返回指向容器末尾(最后一个元素之后)的迭代器。 |
这里重点讲解lower_bound()、upper_bound()和equal_range():
#include<bits/stdc++.h>//万能头,以效率为中心
#include<string>
#include<map>//包含头文件,养成好习惯
/*multimap*/
using namespace std;
/*lower_bound()、upper_bound()和equal_range()*/
void test()
{
multimap<int ,string> mmp{{9,"Dotcpp"},{1,"编程问答"},{9,"计算机二级C语言"},{4,"数据结构教程"},{7,"Java教程"}};//建议这样初始化 ,直观具体
/*lower_bound()返回第一个大于Key的迭代器 */
cout << "lower_bound()找到<key的区间: \n";
for(multimap<int,string>::iterator it = mmp.begin();it!=mmp.lower_bound(7);++it) //迭代器遍历
{
cout << it->second << " ";
}
cout << '\n';
cout << '\n';
/*upper_bound()返回第一个Key的迭代器 */
cout << "upper_bound()找到>Key的区间: \n";
for(multimap<int,string>::iterator it = mmp.upper_bound(4);it!=mmp.end();++it) //迭代器遍历
{
cout << it->second << " ";
}
cout << '\n';
cout << '\n';
/*equal_range()等于key的迭代器区间 */
cout << "equal_range()找到==Key的区间: \n";
auto range = mmp.equal_range(9) ;//auto省略了:pair<multimap<string,string>::iterator,multimap<string,string>::iterator>
for(auto it = range.first;it!=range.second;++it) //迭代器遍历
{
cout << it->second << " ";
}
cout << '\n';
}
int main(){
test();
return 0;
}代码编译结果如下:

总结:multimap与map需要对比起来学习,操作multimap就不能使用'[]'和at(),而且要牢牢掌握lower_bound()、upper_bound()和equal_range()这三个函数,读者需勤加练习,反复记忆和实践。
C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:
一点编程也不会写的:零基础C语言学练课程
解决困扰你多年的C语言疑难杂症特性的C语言进阶课程
从零到写出一个爬虫的Python编程课程
只会语法写不出代码?手把手带你写100个编程真题的编程百练课程
信息学奥赛或C++选手的 必学C++课程
蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程
手把手讲解近五年真题的蓝桥杯辅导课程