set和map的基本使用

news2024/11/17 3:47:08

目录

关联式容器

要点分析

键值对

pair介绍

set

模板参数列表:

set的构造:

 常用接口

操作

multiset

map

map的构造

插入

make_pair

map的迭代器

operator[]

multimap

multimap中为什么没有重载operator[]


关联式容器

关联式容器也是用来存储数据的,和vector、list这样的序列式容器不同的是,它里面存储的是<key,value>结构的键值对,在数据检索时效率很高。在STL库中有两种不同结构的关联式容器,树形结构与哈希结构。树形结构的关联式容器包括set、multiset、map、multimap这四种,也是下面要谈论的主要内容。

要点分析

键值对

要走进关联式容器,首先要理解键值对的概念,这一概念在之前的文章中也有介绍,键值对是用来表示对应关系的结构,结构中一般包含两个成员变量key和value,key代表键值,value代表与之对应的信息。例如:下述图片中的名著和作者就是一一对应的关系!

pair介绍

pair这个类是对键值对的定义,将一对不同类型的数据组合(T1和T2)组合在一起,结构中包含两个成员变量分别是key和value,它们的类型是T1和T2。

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

set

set的底层是用二叉搜索树来实现的。set中只放value,但在底层实际存放的是由<value, value>构成的键值对。

模板参数列表:

●T: set中存放元素的类型,实际在底层存储<value, value>的键值对。
●Compare:set中元素默认按照小于来比较。
●Alloc:set中元素空间的管理方式,使用STL提供的空间配置器管理。 

set的构造:

void TestSet3()
{
	//构造空的set
    set<int> s1;
	//根据区间构造set
	int arr[10] = {0,1,3,4,5,7,9,6,2,8};
	int size = sizeof(arr) / sizeof(int);
	set<int> s2(arr,arr+size);

}

 常用接口

修改操作

插入

void SetInsert()
{
	set<int> ss;
	ss.insert(5);
	ss.insert(3);
	ss.insert(2);
	ss.insert(2);
	ss.insert(4);
	ss.insert(2);
	ss.insert(2);

	for (auto& e : ss)
	{
		cout << e << " ";
	}
}

需要注意的是,当要插入的数据已经存在,将不会继续插入。

删除

void Seterase()
{
	set<int> ss;
	ss.insert(1);
	ss.insert(2);
	ss.insert(3);
	ss.insert(4);
	ss.insert(5);
	for (auto& e : ss)
	{
		cout << e << " ";
	}
	cout << endl;
	ss.erase(3);
	for (auto& e : ss)
	{
		cout << e << " ";
	}
	cout << endl;
	ss.erase(5);
	for (auto& e : ss)
	{
		cout << e << " ";
	}
	cout << endl;
	ss.erase(1);
	for (auto& e : ss)
	{
		cout << e << " ";
	}
}

交换

void setswap()
{
	set<int> s1;
	s1.insert(1);
	s1.insert(3);
	s1.insert(5);
	s1.insert(7);
	s1.insert(9);
	cout << "交换前s1:";
	for (auto& e : s1)
	{
		cout << e << " ";
	}
	set<int> s2;
	s2.insert(2);
	s2.insert(4);
	s2.insert(6);
	s2.insert(8);
	s2.insert(10);
	cout << endl;
	cout << "交换前s2:";
	for (auto& e : s2)
	{
		cout << e << " ";
	}
	s1.swap(s2);
	cout << endl;
	cout << "交换后s1:";
	for (auto& e : s1)
	{
		cout << e << " ";
	}
	cout << endl;
	cout << "交换后s2:";
	for (auto& e : s2)
	{
		cout << e << " ";
	}
}

 清理

void setclear()
{
	set<int> s1;
	s1.insert(2);
	s1.insert(4);
	s1.insert(6);

	s1.clear();
}

 迭代器

void set_iterator()
{
	int arr[10] = {1,3,5,7,9,2,4,6,8,10};
	int size = sizeof(arr) / sizeof(int);
	set<int> s1(arr,arr+size);
	//auto it1 = s1.begin();
	set<int>::iterator it1 = s1.begin();

	while (it1 != s1.end())
	{
		cout << *it1 << " ";
		it1++;
	}

	//auto it2 = s2.rbegin()
	set<int>::reverse_iterator it2 = s1.rbegin();
	cout << endl;
	while (it2 != s1.rend())
	{
		cout << *it2 << " ";
		it2++;
	}
}

 容量

void setempty_size()
{
	int arr[10] = { 1,3,5,7,9,2,4,6,8,10 };
	int size = sizeof(arr) / sizeof(int);
	set<int> s1(arr, arr + size);

	cout << "是否为空:" << s1.empty() << endl;
	cout << "有效元素个数:" << s1.size() << endl;
	s1.clear();
	cout << "是否为空:" << s1.empty() << endl;
	cout << "有效元素个数:" << s1.size() << endl;
}

操作

find: 

void setfind()
{
	int arr[10] = { 1,3,5,7,9,2,4,6,8,10 };
	int size = sizeof(arr) / sizeof(int);
	set<int> s1(arr, arr + size);
	for (auto& e : s1)
	{
		cout << e << " ";
	}
	cout << endl;
	auto it = s1.find(5);
	if (it != s1.end())
	{
		cout <<"找到了:" << *it << endl;
	}
	else
	{
		cout << "没找到!" << endl;
	}
	it = s1.find(100);
	if (it != s1.end())
	{
		cout << "找到了:" << *it << endl;
	}
	else
	{
		cout << "没找到!" << endl;
	}
}

 count:

void setcount()
{
	int arr[10] = { 1,3,1,7,1,2,1,6,8,10 };
	int size = sizeof(arr) / sizeof(int);
	set<int> s1(arr, arr + size);
	for (auto& e : s1)
	{
		cout << e << " ";
	}
	cout << endl;
	cout << s1.count(1) << endl;
	cout << s1.count(2) << endl;
	cout << s1.count(50) << endl;
}

 对于count这个接口,你可能不太理解它为什么会存在,它好像只能确定数据是否出现过,查找数据已经有find了,好像没什么“用武之地”。哈哈哈,它的“志向”当然不在此处,我们接着向下看!

multiset

multiset在接口的用法上大致相同,相比较set而言multiset允许重复数据的出现:

void _multiset()
{
	int arr[10] = { 1,3,1,7,1,2,1,6,8,10 };
	int size = sizeof(arr) / sizeof(int);
	multiset<int> s(arr,arr+size);

	for (auto& e : s)
	{
		cout << e << " ";
	}
	
}

count接口上场:

void _multiset_count()
{
	int arr[10] = { 1,3,1,7,1,2,1,6,2,10 };
	int size = sizeof(arr) / sizeof(int);
	multiset<int> s(arr, arr + size);

	for (auto& e : s)
	{
		cout << e << " ";
	}
	cout << endl;
	cout << s.count(1) << endl;
	cout << s.count(2) << endl;
	cout << s.count(10) << endl;
}

 小问题:既然multiset允许数据的重复,那么当我们查找数据的时候,找到的是哪一个呢?

void _multiset_find()
{

	int arr[10] = { 1,3,1,7,1,2,3,6,3,10 };
	int size = sizeof(arr) / sizeof(int);
	multiset<int> s(arr, arr + size);

	for (auto& e : s)
	{
		cout << e << " ";
	}
	cout << endl;
	multiset<int>::iterator it = s.find(3);
	while (it != s.end())
	{
		cout << *it << " ";
		it++;
	}
}

 测试后发现,find找到的位置是中序遍历第一次找到的元素位置。其余接口和set的使用大多一样,这里就不在测试了。

map

●key: 键值对中key的类型
●T: 键值对中value的类型

map的构造

插入

 查询文档了解到,insert的返回值是一个pair,需要的参数也是一个pair。

void maptest2()
{
	map<string, string> notepad;
	notepad.insert(pair<string,string>("张三","赤峰"));
	notepad.insert(pair<string,string>("李四","北京"));

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

	//auto pr = notepad.insert(pair<string, string>("王麻子", "西安"));
	pair<map<string,string>::iterator,bool> pr = notepad.insert(pair<string, string>("王麻子", "西安"));
	cout << (pr.first)->first <<":"<<(pr.first)->second<< endl;
}

 接收insert后的返回值,并进行分析:

通过上述测试对insert有了大致的了解,但是插入的过程中代码显得好笨重,pair<string,string>......每次都要写好长好长,这个时候就要介绍一个帮我们偷懒的朋友了:make_pair!

make_pair

make_pair的返回值是一个pair,根据模板参数T1,T2确定pair中两个元素的类型,根据参数x,y确定元素值。也就是说在上述代码插入数据的部分可以这样写:

map<string, string> s;
s.insert(make_pair("张小红","内蒙古"));

map的迭代器

void mapTest4()
{
	map<string, string> notepad;
	notepad.insert(make_pair("张三", "赤峰"));
	notepad.insert(make_pair("李四", "北京"));
	notepad.insert(make_pair("王麻子", "西安"));
	notepad.insert(make_pair("张小红", "内蒙古"));

	map<string, string>::iterator it1 = notepad.begin();
	cout << "-----------正向迭代器-----------" << endl;
	while (it1 != notepad.end())
	{
		//cout << (*it1).first << ":" << (*it1).second << endl;
		cout << it1->first << ":" << it1->second << endl;
		it1++;
	}
	cout << "-----------反向迭代器-----------" << endl;
	map<string, string>::reverse_iterator it2 = notepad.rbegin();
	while (it2 != notepad.rend())
	{
		//cout << (*it2).first << ":" << (*it2).second << endl;
		cout << it2->first << ":" << it2->second << endl;
		it2++;
	}
}

operator[]

operator[]重载和以往的[]重载相比有一些不一样,传过来的参数是key,返回值是value。在这过程中,首先向this插入make_pair,insert的返回值类型又是一个pair<iterator,bool>,访问pair的first元素获取到一个迭代器,迭代器找到的位置又是一个pair<key,value>,返回该pair的第二个元素也就是value。

 观察上图分析后,[]除了访问元素外,还能进行查找,修改,插入的操作。

void mapTest5()
{
	map<string,int> grade;
	//插入,前提是该数据原来并不存在
	grade["语文"];
	//[]返回值是value,可以对其进行修改
	grade["语文"] = 99;
	//查找,根据key查找对应的value
	cout << grade["语文"] << endl;
}

应用场景1:统计“象牙山村”选举票数!

void mapset1()
{
	map<string, int> count;

	string arr[10] = {"刘能","谢广坤","谢广坤","赵四","王老七","刘能","刘能","王老七","刘能","谢广坤"};
	
	for (auto& e : arr)
	{
		auto it = count.find(e);
		if (it == count.end())
		{
			//插入
			count.insert(make_pair(e,1));
		}
		else
		{
			//(*it).second++;
			it->second++;
		}
	}
	cout << "-------象牙山选举大会--------" << endl;
	for (auto& e : count)
	{
		cout << e.first << ":" << e.second <<"票"<< endl;
	}
}

 使用[]的版本:

void maptest1()
{
	map<string, int> count;

	string arr[10] = {"刘能","谢广坤","谢广坤","赵四","王老七","刘能","刘能","王老七","刘能","谢广坤"};
	
	for (auto& e : arr)
	{
		count[e]++;
	}
	
	cout << "-------象牙山选举大会--------" << endl;
	for (auto& e : count)
	{
		cout << e.first << ":" << e.second <<"票"<< endl;
	}
}

对比两组代码,后者是不是简单了不少呢,[]使用起来非常的方便,但同时也是一把双刃剑,需要我们对其底层原理有所了解,在使用的时候也一定要细心。

multimap

multimap和map的区别:map中的key是唯一的,而multimap中key是可以重复的。

void Test6()
{
	multimap<string, string> dict;

	dict.insert(make_pair("左边", "left"));
	dict.insert(make_pair("左边", "LEFT"));
	dict.insert(make_pair("左边", "****"));
}

multimap中为什么没有重载operator[]

在了解过map的[]重载后,你知道[]需要key来访问value。那么在multimap这种允许key重复出现的场景下,xxx[key]访问到的value不一定是唯一的,这时的修改操作也不知道该去修改哪一个value,所以multimap没有重载[]。

小结:关联式容器的介绍到这就结束了,有些接口没有涉及到,有兴趣的小伙伴可以继续查找文档探索!

<set> - C++ Reference

<map> - C++ Reference

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

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

相关文章

(五十六)针对主键之外的字段建立的二级索引,又是如何运作的?

上一次我们已经给大家彻底讲透了聚簇索引这个东西&#xff0c;其实聚簇索引就是innodb存储引擎默认给我们创建的一套基于主键的索引结构&#xff0c;而且我们表里的数据就是直接放在聚簇索引里的&#xff0c;作为叶子节点的数据页&#xff0c;如下图。 而且我们现在也对基于主键…

日志框架以及如何使用LogBack记录程序

使用日志框架可以记录一个程序运行的过程和详情&#xff0c;同时便捷地存储到文件里面&#xff0c;并且性能和灵活性都比较好。日志的体系结构包括两类日志规范接口&#xff1a;Commons Logging&#xff0c;简称&#xff1a;JCL&#xff1b;Simple Logging Facade for Java&…

JavaScript高级程序设计读书分享之8章——8.2创建对象

JavaScript高级程序设计(第4版)读书分享笔记记录 适用于刚入门前端的同志 创建Object的实例 let person new Object(); person.name "Nicholas"; person.age 29; person.job "Software Engineer"; person.sayName function() { console.log(this…

增长乏力?创造产品和项目需求的6大方法【一杯咖啡谈项目】

我这里所说的创造需求&#xff0c;类似于PMI在《需求管理实践指南》中所写的专业术语“需要评估”&#xff08;needs assessment&#xff09;&#xff0c;这个需要评估&#xff0c;没有写进PMI的《项目管理知识体系指南&#xff08;PMBOK指南&#xff09;》&#xff08;以下称为…

fork()出来一个进程,这个进程的父进程是从哪来的?

基本概念fork() creates a new process by duplicating the calling process. The new process is referred to as the child process. The calling process is referred to as the parent process.fork()是一个系统调用&#xff0c;不是一个函数。详细信息可以&#xff0c;man…

day(22) Echarts和nacos

day(22) Echarts和nacos一、Echarts和nacos1.1 数据展示1.2 查询日期之间的数据二、配置中心2.1 配置中心spring cloud config2.1.1 缺点2.1.2 其他配置中心2.2 nacos2.2.1 pom2.2.2 配置文件2.2.3 Data id是微服务名称2.2.4 优先级2.2.5 动态刷新2.2.6 namespace2.2.7 多配置文…

Symbiosis Nest 共生巢token跨链兑换协议

参考文献&#xff1a; Getting Started with Symbiosis - Symbiosis Documentation Relayers Network | Symbiosis - Symbiosis Documentation Symbiosis V1 vs. V2 - Symbiosis Documentation Symbiosis protocal 基于稳定币的跨链兑换协议. Symbiosis protocol 组成结构…

pyechart绘制多图(三图及以上)的overlap叠加

pyechart github页面&#xff1a;https://github.com/pyecharts/pyecharts 首先要明确多图叠加到一个图的规则&#xff0c;即多个图只能有一个公共的轴&#xff1a; 比如&#xff0c;横坐标含义相同&#xff08;如时间维度&#xff09;或者&#xff0c;纵坐标取值含义相同 文…

Web3中文|把Web3装进口袋,Solana手机Saga有何魔力?

2月23日&#xff0c;Solana Web3手机Saga发布新的消息&#xff0c;将推出NFT铸造应用程序Minty Fresh。在Minty Fresh&#xff0c;用户仅需轻点并完成拍摄&#xff0c;就可以直接在手机中进行NFT铸造&#xff0c;并在几秒钟内将其转换为链上NFT&#xff0c;NFT还可以发布在 Ins…

STM32学习笔记-SPI

文章目录硬件连接协议层STM32-SPISTM32 SPI框架图SPI初始化结构体SPI 协议是由摩托罗拉公司提出的通讯协议 (Serial Peripheral Interface)&#xff0c;即串行外围设备接口&#xff0c;是一种高速全双工的通信总线。它被广泛地使用在 ADC、LCD 等设备与 MCU 间&#xff0c;要求…

NCRE计算机等级考试Python真题(十一)

第十一套试题1、以下选项对于import保留字描述错误的是&#xff1a;A.import可以用于导入函数库或者库中的函数B.可以使用from jieba import lcut 引入 jieba库C.使用import jieba as jb&#xff0c;引入函数库jieba&#xff0c;取别名jbD.使用import jieba 引入jieba库正确答案…

明明硬件比软件难,但为什么硬件工程师待遇还不如软件?

前言 大家好&#xff0c;最近在知乎上看到一个很有意思的问题&#xff1a; 硬件明明比软件更难&#xff0c;国内的硬件技术也不如软件&#xff0c;为什么硬件工程师待遇还不如软件&#xff1f; 下面分享几位网友的回答&#xff0c;有一定的参考价值&#xff0c;欢迎大家留言讨论…

Dell服务器组Raid + 重装Ubuntu20.0.4

文章目录1. 组建Raid2. 从U盘启动3. 系统安装4. 硬盘分区查看5.后续步骤&#xff1a;1. 组建Raid 1.1. 开机后按CtrlR进入Raid管理界面&#xff1b; 1.2. 选中现有群组后按F2弹出菜单&#xff0c;选择删除现有群组&#xff1b; 1.3. 删除后会列出所有磁盘&#xff0c;仍选…

DSP_TMS320F28377D_ePWM学习笔记

前言 本人需要使用ePWM来控制一个永磁同步电机&#xff08;PMSM&#xff09;, 本文记录了对于TMS320F28377D ePWM模块的学习笔记。主要内容是FOC PMSM控制的ePWM配置&#xff0c;同时包含ADC触发源的配置&#xff0c;关于ADC的学习笔记&#xff0c;请参考DSP_TMS320F28377D_AD…

靶机漏洞那些事儿,这场直播算是讲明白了

CSDN直播间&#xff1a; 小白如何从靶场过渡到实战 ——「业务安全大讲堂第第二季第2期」https://live.csdn.net/room/dingxiangtech/xldogSXD 一名合格的网安工程师&#xff0c;不仅要懂得防漏洞&#xff0c;更要学会找漏洞。 上期直播我们为大家讲解了红队打点与情报收集策…

[busybox] busybox生成一个最精简rootfs(上)

这篇文章是承接着[rootfs]用busybox做一个rootfs(根文件系统)来的&#xff0c;再回看这篇我很久之前写的文章的时候&#xff0c;有一个问题出现在我的脑海中&#xff0c;创建了这个文件那个文件&#xff0c;但确实是每个文件都是必需的吗&#xff1f; 这篇文章我们就来讨论下这…

Graph Neural Network(GNN)图神经网络

Graph Neural Network(GNN)图神经网络&#xff0c;是一种旨在对图结构数据就行操作的深度学习算法。它可以很自然地表示现实世界中的很多问题&#xff0c;包括社交网络&#xff0c;分子结构和交通网络等。GNN旨在处理此类图结构数据&#xff0c;并对图中的节点和边进行预测或执…

PLECS中DLL模块的使用

之前发布了一篇文章&#xff0c;介绍如何使用PSIM中的DLL模块。而本篇文章的内容与之类似&#xff0c;不过主角换成了PLECS。 PLECS和PSIM类似&#xff0c;也属于电力电子仿真软件&#xff0c;使用方便&#xff0c;仿真速度快&#xff0c;和Matlab也有一定的联系&#xff0c;有…

关于Java多线程你了解多少

&#x1f3e1;个人主页 &#xff1a; 守夜人st &#x1f680;系列专栏&#xff1a;Java …持续更新中敬请关注… &#x1f649;博主简介&#xff1a;软件工程专业&#xff0c;在校学生&#xff0c;写博客是为了总结回顾一些所学知识点 目录多线程多线程的创建继承Thread类实现R…

主板EC程序烧写异常致无法点亮修复经验

主板型号&#xff1a;Gigabyte AB350M-Gaming3 官网上明确写着支持R5 5500&#xff0c;但按照如下步骤实践下来实际是不支持的 升级biosF31到F40版本的注意事项&#xff1a; 步骤&#xff1a; 1 使用Q-Flash先将bios升级到f31版本&#xff1b;2 然后下载提示中的ECFW Update To…