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

news2024/11/26 17:47:29

前言

        前面我们学习了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.coloradmin.cn/o/782113.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python实战项目——旅游数据分析(四)

由于有之前的项目&#xff0c;所以今天我们直接开始&#xff0c;不做需求分析&#xff0c;还不会需求分析的可以看我之前的文章。Python实战项目——用户消费行为数据分析&#xff08;三&#xff09; 导入库 import numpy as np import pandas as pd import matplotlib.pyplo…

Sentinel授权规则与规则持久化

&#x1f333;&#x1f333;&#x1f333;&#x1f333;&#x1f333;&#x1f333;&#x1f333; 学习授权规则前&#xff0c;先想想SpringCloud Gateway的黑白名单&#xff0c;请求过网关&#xff0c;gateway会去鉴权。但如果有人把微服务信息泄露出去了呢&#xff1f;此时微…

输出函数print

print("hello world") # 可以输出数字 print(1111) print(2222) # 可以输出字符串 print(helloworld) print("helloworld") # 可以输出运算符的表达式 print(56) # 将数据输出文件中&#xff0c;注意点&#xff1a;1.所指定的盘符存在&#xff0c;2.使…

「苹果安卓」手机搜狗输入法怎么调整字体大小及键盘高度?

手机搜狗输入法怎么调整字体大小及键盘高度&#xff1f; 1、在手机上准备输入文字&#xff0c;调起使用的搜狗输入法手机键盘&#xff1b; 2、点击搜狗输入法键盘左侧的图标&#xff0c;进入更多功能管理&#xff1b; 3、在搜狗输入法更多功能管理内找到定制工具栏&#xff0c…

uni-app中全局设置页面背景颜色

在uni-app中设置背景颜色&#xff0c;想象中应该很简单&#xff0c;其实里面也还是有点小坑的&#xff0c;我们来一起看一下。 方法一&#xff1a;pages.json globalStyle 中设置 backgroundColor 结果&#xff1a;未生效 猜测可能是颜色不能用别名&#xff0c;换十六进制试一…

讯为RK3568开发板入门之-tftpnfs的配置

基础条件 VMware虚拟机 Ubuntu18.04 【网络配置陈桥接模式】 RK3568开发板【我是用讯为的RK3568】 网线连接路由器或者和电脑直连 配置TFTP和NFS的作用 使用tftp和nfs网络挂载可以很方便的进行软件的调试&#xff0c;挂载成功后只要把Ubuntu下编译好的文件程序放到挂载的目录…

有名管道(FIFO)的学习笔记

文章目录 有名管道介绍有名管道的使用创建 注意事项 有名管道介绍 有名管道的使用 创建 命令&#xff0c; mkfifo name函数&#xff0c;int mkfifo(const char *pathname, mode_t mode); 设置错误号&#xff1b; 向管道中写数据&#x1f447;&#xff1a; 从管道读数据&am…

数据库新闻速递 SingleStore Kai for MongoDB有以下6个关键特性, MONGODB 也疯狂

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到3群&#xff08;共…

Hive查询性能优化——物化视图(materialized view)

1. 背景&#xff1a; 在一次大表查询中&#xff0c;查询速度非常慢,插叙时间以分钟为单位&#xff0c;而生产需求要达到10s以内。造成这个问题的原因有多种&#xff0c;性能调优的方式也有多种&#xff0c;这里记录一下其中一种方案&#xff0c;采用物化视图&#xff08;Mater…

[JAVAee]synchronized关键字

目录 1.synchronized的特性 ①互斥性 ②可重入性 2.synchronized的使用示例 ①修饰普通方法 ②修饰静态方法 ③修饰代码块 1.synchronized的特性 ①互斥性 互斥性,就像是给门上锁了一样. 当A线程使用了被synchronized修饰的代码块并对其上锁,其他线程(B线程,C线程)想要使…

从零开始学习自动驾驶路径规划-环境配置

从零开始学习自动驾驶路径规划-环境配置 前面&#xff0c;每个人遇到的问题不一样&#xff0c;这里记录了配置步骤和目前遇到的问题&#xff0c;会持续更新报错解决方法。配置时有报错请认真看报错经验 环境配置步骤&#xff08;18.04和20.04都可以&#xff0c;有些问题没遇到…

流媒体视频融合平台EasyCVR更新版本后,首页无法打开的原因排查与解决

EasyCVR视频融合平台基于云边端一体化架构&#xff0c;可支持多协议、多类型设备接入&#xff0c;包括&#xff1a;NVR、IPC、视频编码器、无人机、车载设备、智能手持终端、移动执法仪等。平台具有强大的数据接入、处理及分发能力&#xff0c;可在复杂的网络环境中&#xff0c…

精通自动化,Pytest自动化测试框架-fixture用例的前后置(实现)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 测试用例实现前后…

软件项目验收测试报告-软件项目验收流程

目录 一、什么是项目验收&#xff1f; 二、软件项目验收材料的准备 三、软件项目验收&#xff0c;按照验收的流程&#xff0c;可分为验收准备、初步验收、最终验收和项目交接四个部分。 对甲方而言&#xff0c;项目验收是正式接受项目成果&#xff0c;将项目从建设转为运营。…

视频增强技术-去噪

本文介绍了关于视频增强技术的相关方法包括传统方法和基于深度学习的方法&#xff0c;并给出了他们的对比实验结果&#xff0c;最后对它们简单的做了总结&#xff0c;文中有一些图片和总结来自于网上其他博主的文章&#xff0c;已在文中标记并给出了相关的原文链接&#xff0c;…

JAVA基础-集合(List与Map)

目录 引言 一&#xff0c;Collection集合 1.1,List接口 1.1.1&#xff0c;ArrayList 1.1.1.1&#xff0c;ArrayList的add&#xff08;&#xff09;添加方法 1.1.1.2&#xff0c;ArrayList的remove&#xff08;&#xff09;删除方法 1.1.1.3&#xff0c;ArrayList的contai…

机器学习之主成分分析(Principal Component Analysis)

1 主成分分析介绍 1.1 什么是主成分分析 主成分分析&#xff08;Principal Component Analysis&#xff09;简称PCA&#xff0c;是一个非监督学习的机器学习算法&#xff0c;主要用于数据的降维&#xff0c;对于高维数据&#xff0c;通过降维&#xff0c;可以发现更便于人类理…

(css)自定义el-dialog对话框添加背景图片

(css)自定义el-dialog对话框添加背景图片 效果&#xff1a; // 文件管理对话框 /deep/ .el-dialog {background: transparent;background-image: url("../assets/image/file-upload-background.png");background-size: 100% 100%; } // 头部 /deep/ .el-dialog__titl…

2024考研408-操作系统 第五章-输入输出IO管理 学习笔记

文章目录 一、I/O管理概述1.1、I/O设备的概念与分类1.1.1、什么是I/O设备&#xff1f;1.1.2、I/O设备的分类&#xff1a;按照使用特性1.1.2、I/O设备的分类&#xff1a;按传输速率分类1.1.3、I/O设备的分类&#xff1a;按照信息交换的单位分类知识点回顾与重要考点 1.2、I/O控制…

【Linux】udp客户端windows版以及Tcp服务器的实现

windows版客户端更适合大多数人~ 文章目录 一. udp客户端windows版二.Tcp服务器的实现总结 一、udp客户端windows版 首先我们将上一篇文章中实现的udp大型聊天室的代码进行修改&#xff0c;注意我们只修改服务端代码将代码修改的很简单就好&#xff0c;因为我们只是做一个如何…