C++ | set与map的用法指南

前言

        前面我们学习了vector、list等容器,其实他们都属于序列式容器,因为其底层为线性结构;今天我们学习使用的set与map是属于关联式容器,关联式容器更注重于数据检索访问的效率;本文所有的资料均查阅于文档,以下为文档链接;可自行查看;

文档链接

一、set的简介

        在C++中,它是一种集合,是我们前面二叉搜索树的Key模型;其所在的头文件是set;其实这个头文件下,还有一个兄弟容器 ------- multiset,在介绍set是我也会介绍multiset,因为二者其实用法区别并不大,差异我会一 一介绍出来;

        关于multiset其实与set几乎差不多,唯一区别是,set会去重,而multiset不会;本文主要注重set的讲解,当然,你会使用set,multiset你也自然会使用了;

        set的类模板如上所示;我们说过它是我们前面学过二叉搜索树的Key模型;因此它的第一个模板参数就是我们的Key的类型;第二个模板参数是比较的方式,需要传入一个仿函数类;第三个参数为内存池相关对象;传默认即可;

        首先我们查看该类的类型重定义,我们发现该类有key_type与value_type都是第一个模板参数T类型;所以说set是Key模型;然后我们发现set的迭代器是双向迭代器;

二、set相关接口

1、构造相关接口

        这部分我们主要看其构造函数与赋值重载的接口;构造函数接口如下;我们发现有三种不同的构造方式,第一种是无参的默认构造;第二种是迭代器区间构造;第三种是拷贝构造;有了前面的基础,这里的接口几乎看一眼都会使用了; 

void test_set3()
{
	// 无参构造
	set<int> s1;
	// 迭代器区间构造
	vector<int> v{ 1,2,3,4,5,6 }; // C++11初始化语法
	set<int> s2(v.begin(), v.end());
	// 拷贝构造
	set<int> s3(s2);
}

        赋值重载接口如下;赋值重载对于已存在的对象进行赋值;使用难度几乎没有;

2、迭代器相关接口

        迭代器真的不愧是STL中六大组件之一,无论是线性结构还是set与map中的树形结构,我们都可以通过迭代器接口来进行访问;使用起来几乎没有区别;这里迭代器为双向迭代器,因此不支持方括号的方式访问,但支持++与--;使用如下;

void test_set4()
{
	set<int> s1;
	// 这里为了展示迭代器先使用插入接口了
	s1.insert(2);
	s1.insert(3);
	s1.insert(1);
	s1.insert(5);
	s1.insert(2);
	s1.insert(1);
	s1.insert(4);

	set<int>::iterator it = s1.begin();
	while (it != s1.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
	
	// 既然支持迭代器,就肯定有范围for了
	for (auto& e : s1)
	{
		cout << e << " ";
	} 
	cout << endl;
}

        我们发现对set用迭代器遍历的时候,set对里面的数据完成了去重+排序;我们再试试我们之前说的multiset,代码与结果如下;

void test_set4()
{
	cout << "set: " << endl;
	set<int> s1;
	// 这里为了展示迭代器先使用插入接口了
	s1.insert(2);
	s1.insert(3);
	s1.insert(1);
	s1.insert(5);
	s1.insert(2);
	s1.insert(1);
	s1.insert(4);

	set<int>::iterator it = s1.begin();
	while (it != s1.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
	
	// 既然支持迭代器,就肯定有范围for了
	for (auto& e : s1)
	{
		cout << e << " ";
	} 
	cout << endl;


	cout << "multiset: " << endl;
	multiset<int> ms1(s1.begin(), s1.end());
	ms1.insert(2);
	ms1.insert(3);
	ms1.insert(1);
	ms1.insert(5);
	ms1.insert(2);
	ms1.insert(1);
	ms1.insert(4);
	for (auto& e : ms1)
	{
		cout << e << " ";
	}
	cout << endl;
}

        我们发现与set不同,multiset只会对数据进行排序,并不具有去重功能; 

3、容量相关接口

        到了这一块就没有什么扩容的概念了;empty是检查set是否为空,size是查看set中元素个数,max_size是查看理论上最多可以放的元素个数,这个接口几乎没什么意义;

 4、修改相关接口

关于set的修改接口如下所示;以下展示比较经常使用的接口;

void test_set5()
{
	set<int> s1;
	// 插入某个值
	s1.insert(2);
	s1.insert(5);
	s1.insert(1);
	s1.insert(2); // 插入失败,因为有2这个值了
	s1.erase(1); // 删除某个值
	set<int> s2;
	// 交换两个set
	s1.swap(s2);
	// 清空某个set
	s2.clear();
}

5、其他相关操作 

关于set还有一些特殊化的操作,如下所示;

void test_set6()
{
	set<int> s1;
	s1.insert(0);
	s1.insert(1);
	s1.insert(3);
	s1.insert(3);
	s1.insert(4);
	// 查找某个key的位置,返回该位置迭代器,若没找到返回s1.end()
	set<int>::iterator it = s1.find(2);
	if (it != s1.end())
	{
		cout << "找到了" << endl;
	}

	// count接口在set中就是查看该元素在不在set中
	cout << s1.count(2) << endl;
}

        这里的count在multiset中是返回指定元素个数;lower_bound函数与upper_bound函数是一个查找函数,一个是返回一个迭代器,该迭代器指向集合容器中的第一个元素,该元素不被认为在值之前。另一个是返回一个迭代器,该迭代器指向集合容器中的第一个元素,该元素被认为是在值之后。equal_range则是返回一个范围的边界,该范围包括集合容器中与 value 等效的所有元素。

三、set的应用场景

        set是一个Key模型,因此我们通常用来解决在不在的问题;例如还是查一篇文章中的单词是否拼写正确,我们可以检查这个单词是否存在字典中;如下代码所示;

void test_set2()
{
	set<string> dict;
	dict.insert("left");
	dict.insert("right");
	dict.insert("area");
	dict.insert("count");
	dict.insert("return");

	string str;
	while (cin >> str)
	{
		set<string>::iterator pos = dict.find(str);
		if (pos != dict.end())
		{
			cout << "在" << endl;
		}
		else
		{
			cout << "不在" << endl;
		}
	}

}

四、map简介 

        map是我们前面所学二叉搜索树中的一种Key_Value模型,是一种键值对的方式,key与value一 一对应;与set类似,还有一个multimap接口;因为map也是去重的,multimap则是不去重版本;它们的接口使用几乎也无二异;故放在一起讲解;

        首先我们看模板参数,第一个模板参数是Key的类型,第二个模板参数是Value的类型;第三个参数则是比较的方式;第四个模板参数是内存池相关类;

        在看map类型的重定义时,我们发现这里与set有很大的差别;它的key_type是第一个模板参数;这里多了一个map_type,是第二个模板参数;而它的value_type则是一个二元组(pair),里面存的是一个键值对;而迭代器是一个双向迭代器;

        在正式谈map接口之前,我们首先弄清关于pair,pair是一个二元组,具体定义如下;其中有两个成员分别为first与second;

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)
    {}
};

        同时,C++还为我们提供了一个函数来构建pair;我们可以通过这个函数实例化出一个pair对象;

五、map相关接口

1、构造函数相关接口

        map的构造函数无非也就是那三种构造方式,默认构造,迭代器区间构造,拷贝构造;

 2、迭代器相关接口

        迭代器的使用也没什么不同,演示代码如下;

void test_map1()
{
	map<string, string> dict;
	dict.insert(make_pair("left", "左边"));
	dict.insert(make_pair("right", "右边"));
	dict.insert(make_pair("count", "计数"));

	map<string, string>::iterator it = dict.begin();
	while (it != dict.end())
	{
		// 迭代器本质实际上就是指向pair的指针,因此这里我们指定访问的某个具体成员
		cout << it->first << ": " << it->second << endl;
		it++;
	}
	cout << endl;

	// 范围for
	for (auto& e : dict)
	{
		cout << e.first << ": " << e.second << endl;
	}
	cout << endl;
}

3、容量与修改相关接口

        容量接口与前set相同,此处就不做介绍了;

        主要介绍这里的修改相关接口,这里的insert插入的是value_type类型的数据,也就是我们前面说的pair类型的数据; 

        注意,上面的插入数据的接口是一个二元组的pair类型的数据,且pair的第一个数据的类型是一个迭代器,第二个数据的类型是一个bool类型的数据;实际上,第二个pair的第二个成员是记录是否插入成功,若插入数据的key值已存在,则插入失败,第二个参数设置为false,第一个参数设置为对应key的迭代器;若插入数据不存在,则插入新值,然后第一个参数设置为新插入key值得迭代器的位置,第二个参数设置为true; 

void test_map2()
{
	map<string, string> dict1;
	dict1.insert(make_pair("left", "左边"));
	dict1.insert(make_pair("right", "右边"));
	dict1.insert(make_pair("count", "计数"));

	dict1.insert(make_pair("right", "权力")); // 插入失败

	// 删除指定键值对
	dict1.erase("count");

	// 交换两个map(两棵树的根节点)
	map<string, string> dict2;
	dict1.swap(dict2);
	// 清空
	dict2.clear();
}

4、方括号访问与其他函数

        关于map的使用,最需要注意的就是这个方括号了;其次我们需要学会使用这个find接口;这里的count接口与set一样,查看一个键值对是否存在,在multimap可以统计某个key在容器中的个数;

        方括号的返回值的声明如下所示,其实际上调用了insert函数而已;函数体也放在下面了;

        函数体如下图,可能很多人看到下面的代码头都大了,小编一开始看到这样的代码也是一脸懵;

mapped_type& operator[] (const key_type& k)
{
    return (*((this->insert(make_pair(k,mapped_type()))).first)).second;
}

        这个知识不就跟我们之前学的插入的返回值串起来了吗?map中的方括号功能十分强大,如下所示;

void test_map3()
{
	map<string, string> mp1;
	mp1.insert(make_pair("left", "左边"));
	mp1.insert(make_pair("right", "右边"));
	mp1.insert(make_pair("int", "整型"));
	mp1.insert(make_pair("right", "权力")); // 插入失败

	// 我们可以通过方括号代替insert
	mp1["double"] = "浮点型"; // 插入
	cout << mp1["int"] << endl; // 查找
	mp1["right"] = "权力";  // 查找+修改
}

        方括号语法确实也强大,具有多种功能;接下来我们来看看map的查找函数;

void test_map4()
{
	map<string, string> mp1;
	mp1.insert(make_pair("left", "左边"));
	mp1.insert(make_pair("right", "右边"));
	mp1.insert(make_pair("int", "整型"));

	map<string, string>::iterator pos = mp1.find("int");
	if (pos != mp1.end())
	{
		cout << pos->second << endl;
	}
}

        相比起来,还是我们的方括号似乎更加好用;

五、map的应用场景

        map是key_value模型,主要用于键值对的维护,是一种一 一对应的关系;我们可以用来统计某种物品的个数,这里我们在之前的二叉搜索树那里也实现过相同的代码,今天我们用map再实现一次;

void test_map5()
{
	map<string, int> fruits;
	std::string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "火龙果", "苹果", 
		"西瓜","苹果", "香蕉", "苹果", "香蕉" , "火龙果"};
	// 方法一
	//for (auto& e : arr)
	//{
	//	auto pos = fruits.find(e);
	//	if (pos != fruits.end())
	//	{
	//		pos->second++;
	//	}
	//	else
	//	{
	//		fruits.insert(make_pair(e, 1));
	//	}
	//}
	
	// 方法二
	for (auto& e : arr)
	{
		fruits[e]++;
	}


	for (auto& e : fruits)
	{
		cout << e.first << ": " << e.second << endl;
	}
	cout << endl;
}

        这里是不是又能体会到方括号功能的强大!同时这里也体现了key_value模型的作用;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/43052.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

RocketMQ深入分析

RocketMQ深入分析 1. 消息存储 目前的MQ中间件从存储模型来&#xff0c;分为需要持久化和不需要持久化的两种模型&#xff0c;现在大多数的是支持持久化存储的&#xff0c;比如ActiveMQ、RabbitMQ、Kafka、RocketMQ&#xff0c;ZeroMQ却不需要支持持久化存储而业务系统也大多…

BART模型和 Electra模型对比

总结 Electra模型在使用较少的计算资源的情况下能够达到跟大语言模型相近的效果。但BART模型对于传统的BERT中加入了不同种制造noise的方式&#xff0c;是BERT和GPT的结合体。Electra模型主要是Generator模型和Discriminator模型的结合体。 未知参数设置&#xff0c;两个模型…

【历史上的今天】7 月 20 日:人类登上月球;数据仓库之父诞生;Mac OS X Lion 发布

整理 | 王启隆 透过「历史上的今天」&#xff0c;从过去看未来&#xff0c;从现在亦可以改变未来。 今天是 2023 年 7 月 20 日&#xff0c;在 2005 年的今天&#xff0c;时任微软全球副总裁的李开复加盟谷歌担任谷歌全球副总裁及中国区总裁。谷歌公司在发布聘请李开复消息的同…

Rust之通用编程

1、变量与可变性&#xff1a; 在Rust语言中&#xff0c;变量默认是不可变的&#xff0c;所以一旦变量被绑定到某个值上面&#xff0c;这个值就再也无法被改变。 可以通过在声明的变量名称前添加mut关键字来使其可变。除了使变量的值可变&#xff0c;mut还会向阅读代码的人暗示…

【机器学习】分类算法 - KNN算法(K-近邻算法)KNeighborsClassifier

「作者主页」&#xff1a;士别三日wyx 「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;零基础快速入门人工智能《机器学习入门到精通》 K-近邻算法 1、什么是K-近邻算法&#xff1f;2、K-近邻算法API3、…

【论文阅读 03】机器学习算法在颈动脉斑块影像学分类中的研究进展

读完之后就是&#xff0c;总结 机器学习&#xff08;SVM、小波&#xff09;和深度学习&#xff08;CNN&#xff09;在 颈动脉斑块影像学中的 分类效果。只讨论了超声、磁共振两种成像 Chin J Clin Neurosci 临床神经科学杂志 复旦大学 颈动脉斑块( carotid plaques) 是一种…

opencv 之 外接多边形(矩形、圆、三角形、椭圆、多边形)使用详解

opencv 之 外接多边形&#xff08;矩形、圆、三角形、椭圆、多边形&#xff09;使用详解 本文主要讲述opencv中的外接多边形的使用&#xff1a; 多边形近似外接矩形、最小外接矩形最小外接圆外接三角形椭圆拟合凸包 将重点讲述最小外接矩形的使用 1. API介绍 #多边形近似 v…

若依微服务整合activiti7.1.0.M6

若依微服务3.6.3版本整合activiti7&#xff08;7.1.0.M6&#xff09; 目前有两种办法集成activiti7 放弃activiti7新版本封装的API&#xff0c;使用老版本的API&#xff0c;这种方式只需要直接集成即可&#xff0c;在7.1.0.M6版本中甚至不需要去除security的依赖。不多介绍&a…

Origin科学绘图分析软件2023最新版下载安装教程

在科学研究和工程领域&#xff0c;数据的处理和分析是至关重要的一环&#xff0c;而Origin则是这方面的一款重要工具。Origin软件是由OriginLab公司开发的&#xff0c;主要用于各种科学数据的处理和分析&#xff0c;以及高质量的科学图形的创建。#乐享周末分享吧# 下载地址文末…

【英杰送书第三期】Spring 解决依赖版本不一致报错 | 文末送书

Yan-英杰的主 悟已往之不谏 知来者之可追 C程序员&#xff0c;2024届电子信息研究生 目录 问题描述 报错信息如下 报错描述 解决方法 总结 【粉丝福利】 【文末送书】 目录&#xff1a; 本书特色&#xff1a; 问题描述 报错信息如下 Description:An attempt…

微服务保护——Sentinel【实战篇二】

一、线程隔离 &#x1f349; 线程隔离有两种方式实现&#xff1a; 线程池隔离信号量隔离&#xff08;Sentinel默认采用&#xff09; 线程隔离&#xff08;舱壁模式&#xff09;&#x1f95d; 在添加限流规则时&#xff0c;可以选择两种阈值类型&#xff1a; QPS&#xff1a;…

Java反射 -- 详细介绍 (框架核心)

反射 是 Java框架 的核心 &#xff0c;无论是Tomcat、SpringMVC、Spring IOC、Spring AOP、动态代理 &#xff0c;都使用到了 反射 反射的作用简单讲就是 无需 new 对象&#xff0c;就可以动态获取到一个类的全部信息&#xff0c;包括 属性、方法&#xff0c;构造器&#xff0…

3、C# 方法构成

上一节,我们讲述了程序的基本构成。由大到小分别为”解决方案-->项目-->类-->方法“。 这一节,我们讲讲方法。 方法可以说是程序的基本构成单位。假如把方法抽象成点的话,我们可以认为程序是一个树状的结构。树根,就是我们的起点方法,也叫主方法。这一点,基本…

iOS 测试 iOS 端 Monkey 测试

说起 Monkey 测试&#xff0c;大家想到的是 monkey 测试只有安卓有&#xff0c;monkey 测试只针对安卓 app&#xff0c;今天给大家分享一下 Monkey 测试在 iOS 端也能跑&#xff01;iOS 端 app 也能使用 Monkey 测试来执行稳定性测试。 一、环境准备 1、准备 Mac 设备&#x…

SpringCloud分布式项目下feign的使用

新建一个feign的微服务&#xff08;后面统称为A&#xff09;&#xff0c;其他项目要使用利用maven导入该服务模块的依赖就行了 导入依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</…

Tabby - 本地化AI代码自动补全 - Linux Debian

参考&#xff1a; https://github.com/TabbyML/tabby Docker | Tabby Linux Debian上快速安装Docker并运行_Entropy-Go的博客-CSDN博客 Tabby - 本地化AI代码自动补全 - Windows10_Entropy-Go的博客-CSDN博客 为什么选择Tabby 已经有好几款类似强劲的代码补全工具&#xf…

【苹果日历推送】群发部署开发工具、SDK或框架,如APNs推送服务的HTTP/2接口

苹果日历本身并不直接支持群发推送通知&#xff0c;因为推送通知是针对单个设备的。如果你想向多个用户发送推送通知&#xff0c;你需要在自己的应用中实现推送功能&#xff0c;然后针对每个设备单独发送推送通知。 以下是实现推送通知的一般步骤&#xff1a; 开发推送服务&a…

【Linux | Shell】结构化命令2 - test命令、方括号测试条件、case命令

目录 一、概述二、test 命令2.1 test 命令2.2 方括号测试条件2.3 test 命令和测试条件可以判断的 3 类条件2.3.1 数值比较2.3.2 字符串比较 三、复合条件测试四、if-then 的高级特性五、case 命令 一、概述 上篇文章介绍了 if 语句相关知识。但 if 语句只能执行命令&#xff0c…

vscode(Better Comments插件)在vue文件中不显示相对应的颜色

解决办法&#xff1a; 1、在.vscode文件下找到 aaron-bond.better-comments-3.0.2 &#xff08;我的路径&#xff1a;C:\Users\cown\.vscode\extensions\aaron-bond.better-comments-3.0.2&#xff09;&#xff0c;后面版本不唯一&#xff0c;根据自身情况辨别 2、进入文件路…

vue项目展示pdf文件

记录贴 最近我有个需求,就是在h5页面上展示pdf文件,分页,最后一页有个蒙层阴影渐变的效果,尝试过一些插件,但都不是很好用,最后用了pdfjs-dist加上canvas 可以看下效果 先下载: npm i pdfjs-dist2.5.207下面展示代码 html: <template><canvas v-for"pageNumb…