【c++丨STL】map/multimap的使用

🌟🌟作者主页:ephemerals__
🌟🌟所属专栏:C++STL

目录

前言

一、map/multimap的介绍

二、map的默认成员函数

构造函数constructor

析构函数destructor

赋值重载

 三、map的迭代器接口

四、map的容量相关接口

empty

size

五、map的元素访问接口

operator[ ]

at

六、map的增删相关接口 

insert

erase

swap

clear

七、map的其他操作接口

find

count

lower_bound、 upper_bound 和 equal_range

八、map的具体使用

总结


前言

        之前我们学习了STL关联式容器——set/multiset的使用,本篇文章我们将介绍另一组关联式容器map(映射表)/multimap(多重映射表)

一、map/multimap的介绍

        与set相同,map的底层也是基于红黑树实现的,其内部元素根据自动升序排列

但两者有如下区别

存储内容:set存储的是,而map存储的是键值对(数据元素是一个pair

元素访问:set只能访问,而map可以通过键来访问对应的值,并且值可以支持修改

因此,set适用于唯一元素的集合操作,如去重;而map更适用于处理键值关系

        相比map,multimap支持多个相同键存在。

        map和multimap相关接口查阅:

<map> - C++ Reference

        map和multimap的使用方法基本相同,但相比multimap,map更加常用,所以接下来的内容将主要聚焦于map常用接口的使用方法,而在与multimap出现显著区别时,我们会适当提及它。

注意:在使用map/multimap时,要引头文件<map>,且该容器定义在命名空间std当中。

二、map的默认成员函数

构造函数constructor

map有五种构造函数,其中较为常用的有如下四个:

函数原型功能说明
map();无参构造
map(InputIterator first, InputIterator last);迭代器区间构造
map(const map& x);拷贝构造,用一个map对象构造另一个map对象
map(initializer_list<value_type> il);初始化器构造

代码示例:

#include <map>
#include <string>
#include <iostream>
using namespace std;

int main()
{
	map<string, int> m1;//无参构造,创建一个map对象,映射关系为string,int

	map<string, int> m2({ {"hello",1},{"hehe",3},{"haha",2} });//初始化器构造

	map<string, int> m3(m2);//拷贝构造

	map<string, int> m4(++m3.begin(), --m3.end());//迭代器区间构造
	return 0;
}

析构函数destructor

析构函数在对象生命周期结束时自动调用,会销毁容器中的所有元素,并使用其分配器释放map容器分配的所有存储空间。

赋值重载

将新内容赋值给容器,替换当前内容。 

代码示例:

#include <map>
#include <string>
#include <iostream>
using namespace std;

int main()
{
	map<int, int> m1 = { {1,1},{2,2},{3,3} };
	map<int, int> m2;
	map<int, int> m3;

	m2 = m1;//对象赋值
	m3 = { {1,1},{2,2},{3,3} };//初始化器赋值
	return 0;
}

 三、map的迭代器接口

map的迭代器接口使用方法与之前学过的容器都相同,这里就不多赘述。

        需要注意以下几点:

1. map的迭代器是双向迭代器

2. map迭代器进行顺序遍历的结果是有序的,这归因于它采用了红黑树的中序遍历算法。

3. 无论是使用普通迭代器还是const迭代器,都无法修改map元素的键key,但是普通迭代器可以修改value

四、map的容量相关接口

empty

判断map容器是否为空,若为空,则返回true;否则返回false。

这个函数不会以任何方式修改容器。

代码示例:

#include <map>
#include <string>
#include <iostream>
using namespace std;

int main()
{
	map<int, int> m = { {1,1},{2,2},{3,3} };
	
	cout << m.empty() << endl;//判空
	return 0;
}

 

size

 size返回map容器内的元素个数

代码示例:

#include <map>
#include <string>
#include <iostream>
using namespace std;

int main()
{
	map<int, int> m = { {1,1},{2,2},{3,3} };
	
	cout << m.size() << endl;
	return 0;
}

五、map的元素访问接口

由于map元素是以键值对的方式存储,所以其相比set多了元素访问接口,我们可以使用该接口通过键来访问值。两个接口的详细讲解如下:

operator[ ]

operator[]在map当中的功能十分强大,它不仅支持根据键访问值,还支持元素的插入

首先,对于该重载函数,我们应将键key作为下标来传参

如果容器内已经有相同的key,那么该函数返回value的引用,达到根据键访问或修改值的效果。

如果容器内不存在key,那么该函数就会在容器中插入一个带有该key的新元素,并返回value的引用。此时我们将其返回值进行修改,就达到了插入一个键值对的效果。注意:若我们没有修改其返回值,容器中的元素个数也会+1,此时value的值为默认构造值

调用这个函数等价于以下语句:

(*((this->insert(make_pair(k,mapped_type()))).first)).second

使用示例:

#include <map>
#include <string>
#include <iostream>
using namespace std;

int main()
{
	map<int, int> m = { {1,1},{2,2},{3,3} };
	
	m[3] = 0;//将键3的映射值修改为0

	m[4] = 4;//插入一个键值对{4,4}

	return 0;
}

at

与operator[ ]不同,at仅仅支持根据键访问或修改值,但不支持元素插入。我们将key作为参数传入,返回值是valus的引用。

当容器内不存在相同的key时,该函数会抛出异常

使用示例:

#include <map>
#include <string>
#include <iostream>
using namespace std;

int main()
{
	map<int, int> m = { {1,1},{2,2},{3,3} };

	m.at(3) = 0;//将键3的映射值修改为0

	m.at(4) = 4;//找不到4,抛出异常
	return 0;
}

注意:对于multimap,不存在operator[ ]和at这两个函数。

六、map的增删相关接口 

insert

insert的作用是插入元素,增加容器的大小。支持单个键值对插入、迭代器区间插入和初始化器插入。

因为map中元素的键是唯一的,所以插入操作会检查每个被插入元素的键是否与容器中已经存在的元素的键相等,如果相等,则不插入该元素,并返回包含重复键的元素的迭代器(如果该重载函数有返回值)

这里介绍一下第一个重载函数(单个元素插入)的返回值:是一个pair,当插入成功时,pair的第一个元素是指向新元素的迭代器,第二个元素是true;当因有重复键而插入失败时,pair的第一个元素是指向包含重复键的元素的迭代器,第二个元素是false。

使用示例:

#include <map>
#include <string>
#include <iostream>
using namespace std;

int main()
{
	map<int, int> m;
	map<int, int> m2({ {3,3},{4,4} });

	//单个元素插入
	m.insert({ 1,1 });
	m.insert(make_pair(2, 2));

	//迭代器区间插入
	m.insert(m2.begin(), m2.end());

	//初始化器插入
	m.insert({ {5,5},{6,6} });
	return 0;
}

在map当中,使用operator[ ]插入元素的方法会比insert更加常用。

对于multimap的insert函数,其插入方法与map相同。并且即使有重复键,也会插入成功。

erase

erase的作用是删除map中的元素包括迭代器指定删除、按键删除和迭代器区间删除

对于按键删除的重载函数,它的返回值是被成功删除的元素数量,它的作用在支持重复键的multimap中较大。 

使用示例:

#include <map>
#include <string>
#include <iostream>
using namespace std;

int main()
{
	map<int, int> m = { {1,1},{2,2},{3,3} };

	m.erase(1);//按键删除

	m.erase(m.begin());//迭代器指定位置删除

	m.erase(m.begin(), m.end());//迭代器区间删除
	return 0;
}

swap

swap用于交换两个map容器的内容。使用方法与其他容器相同,不多赘述。

clear

清空map容器内的所有元素。 

七、map的其他操作接口

find

find用于按键查找元素如果找到了就返回指向该元素的迭代器,否则返回尾迭代器。 

count

count的作用是获取容器中键key所在元素的出现次数对于不允许键重复的map,它只返回0(表示不存在)或1(表示存在),可以用于判断某个元素是否在容器当中。而对于multimap,可以统计数量

lower_bound、 upper_bound 和 equal_range

这三个函数的使用方法请参照这篇文章:

【c++丨STL】set/multiset的使用-CSDN博客

它们的使用方法、允许重复元素出现时的逻辑与set/multiset基本相同,并且并不是很常用,注意对于map/multimap而言是以键key为查找标准即可。

八、map的具体使用

        接下来我们使用map来解决一个具体问题:有一个字符串数组,其中包含各种水果的名称,统计每种水果的出现次数

代码如下:

#include <map>
#include <string>
#include <iostream>
using namespace std;

int main()
{
	string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };

	map<string, int> m;//创建map,映射关系为<string,int>,键表示水果名称,值表示该水果出现次数

	//遍历字符串数组
	for (auto& str : arr)
	{
		m[str]++;//统计
	}

	//遍历map
	for (auto& kv : m)
	{
		cout << kv.first << ':' << kv.second << endl;
	}
	return 0;
}

运行结果:

我们巧妙地利用了operator[ ]函数,当遇到新水果时,插入{水果,0},然后将其自增,出现次数变为1;当遇到已有水果时,直接将出现次数进行累加。这样就达到了统计每种水果的出现次数的效果,最后遍历输出即可。

总结

        本篇文章,我们讲解了STL中的另一对关联式容器map/multimap的使用方法与具体场景。之后博主会和大家一起,在实现红黑树的基础上尝试模拟实现set和map两种容器。如果你觉得博主讲的还不错,就请留下一个小小的赞在走哦,感谢大家的支持❤❤❤ 

python+opencv简谱识别音频生成系统源码含GUI界面+详细运行教程+数据 一、项目简介 提取简谱中的音乐信息,依据识别到的信息生成midi文件。 Extract music information from musical scores and generate a midi file according to it. 二、项目运行环境 python=3.11.1 第三方库依赖 opencv-python=4.7.0.68 numpy=1.24.1 可以使用命令 pip install -r requirements.txt 来安装所需的第三方库。 三、项目运行步骤 3.1 命令行运行 运行main.py。 输入简谱路径:支持图片或文件夹,相对路径或绝对路径都可以。 输入简谱主音:它通常在第一页的左上角“1=”之后。 输入简谱速度:即每分钟拍数,同在左上角。 选择是否输出程序中间提示信息:请输入Y或N(不区分大小写,下同)。 选择匹配精度:请输入L或M或H,对应低/中/高精度,一般而言输入L即可。 选择使用的线程数:一般与CPU核数相同即可。虽然python的线程不是真正的多线程,但仍能起到加速作用。 估算字符上下间距:这与简谱中符号的密集程度有关,一般来说纵向符号越稀疏,这个值需要设置得越大,范围通常在1.0-2.5。 二值化算法:使用全局阈值则跳过该选项即可,或者也可输入OTSU、采用大津二值化算法。 设置全局阈值:如果上面选择全局阈值则需要手动设置全局阈值,对于.\test.txt中所提样例,使用全局阈值并在后面设置为160即可。 手动调整中间结果:若输入Y/y,则在识别简谱后会暂停代码,并生成一份txt文件,在其中展示识别结果,此时用户可以通过修改这份txt文件来更正识别结果。 如果选择文件夹的话,还可以选择所选文件夹中不需要识别的文件以排除干扰
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值