C++STL详解(九)map和set的使用

一.关联式容器的介绍

C++STL包含了序列式容器关联式容器

  1. 序列式容器里面存储的是元素本身,其底层是线性的数据结构,就譬如我们之前学习的vector,list,deque等等。
  2. 关联式容器里面存储的是<key,value>的键值对,在数据检索时比序列式容器效率更高。就譬如我们现在要学习的map和set等。

这里需要给大家提醒的一点是:

我们之前学习的queue、stack、priority_queue属于容器适配器,他们会使用别的容器来适配。

下面,我们开始介绍下键值对这个东西。
键值对是用来表示一一对应关系的一种结果,这个结构中一般是包含两个成员变量key和value。

  • key表示键值。
  • value表示与key对应的信息。

二.键值对

在SGI-STL中,关于键值对的定义如下所示:

template<class K,class V>
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)
	{}
};

pair中的first表示键值second则表示实值,在给关联式容器插入数据时,会构建pair对象
就譬如以下代码,就可以构建出一个键值key为string实值value为int的匿名键值的键值对pair对象。

pair<string,int>("haha",1);

这样的写法有些繁琐,因此库中设计了一个函数模板make_pair,可以根据传入的参数调用pair构建对象并返回,如下:

make_pair("hehe",123);

下面,我们给出make_pair的定义:

template<class T1,class T2>
pair<T1,T2> make_pair(T1 x,T2 y)
{
	return (pair<T1,T2>(x,y));
}

这个函数在实际应用时,会被编译器优化为内联函数,因此不会产生太大的消耗。
比如我们若是要创建一个字典,那么这个字典中的英文单词与中文含义应该是一一对应的,也就是说,我们应该能够通过单词找到它对应的中文含义。
这样的情况,我们就可以利用键值对来完成这个任务。

三.set的使用

3.1set的定义

在我们之前介绍二叉搜索树时,我们大家可以发现其的模板参数只用了一个T1,而我们的set的底层就是一种特殊的二叉树,我们可以将其理解为只有一个实值value的特殊模型
下图为cpp库中set的定义:
在这里插入图片描述
其中:

  • 参数1:T即是set的实值
  • 参数2:compare是中序遍历结果的排列,默认是升序
  • 参数3:空间配置器

下面,我们来学习以下set的定义方式:
在这里插入图片描述
根据CPP官方文档,我们可以总结出如下的定义方式:
方式1:构造一个空的容器

set<int> s1;

方式2:拷贝构造

set<int> s1(s2);

方式3:迭代器区间构造

vector<int> s2={1,2,3,4,5,6,7,8,9,10};
set<int> s1(s2.begin(),s2.end());

方式4:构造空容器,并指定比较方式

set<int,greater<int>> s4;

3.2迭代器

作为STL的容器,set也是支持迭代器操作的。
对于set的迭代器而言,我们需要掌握的是以下的内容:

  • 二叉搜索树的中序遍历为有序,因此我们这里的迭代器++应该是到中序的下一个结点
  • set的迭代器是一个双向迭代器, 是支持++和–的。

我们可以在官方文档中看到如下内容:
在这里插入图片描述
另外,由于我们使用的是平衡搜索二叉树,因此它是不支持修改的,因此set中的迭代器都是const的,都是不能修改的。
如下:
在这里插入图片描述

3.3set的使用

下面我们来看看set的相关操作:

功能用途
迭代器遍历容器
empty判断是否为空
size返回元素个数
max_size返回最大存储量
insert指定位置元素插入
erase删除
swap交换两个容器
clear清空容器内的元素
find返回寻找到的元素的迭代器位置
count返回容器指定键值的数量

下面,我们通过下面的这段代码来实践下:

#include <iostream>
#include <map>
#include <vector>
#include <set>
using namespace std;
int main()
{
	vector<int> arr1 = { 7,8,9,6,5,4,1,2,3 };
	set<int> s1(arr1.begin(), arr1.end());
	//迭代器遍历
	cout << "迭代器遍历结果:" << endl;
	set<int>::iterator it = s1.begin();
	while (it != s1.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
	
	cout << "---------------------------" << endl;
	cout << "empty:" << s1.empty() << endl;
	cout << "size:" << s1.size() << endl;
	cout << "max_size:" << s1.max_size() << endl;
	//数据插入
	cout << "---------------------------" << endl;
	cout << "insert(5):";
	s1.insert(5);
	for (auto e : s1)
	{
		cout << e << ' ';
	}
	cout << endl;
	//数据删除
	cout << "---------------------------" << endl;
	cout << "erase(5):";
	s1.erase(5);
	for (auto e : s1)
	{
		cout << e << ' ';
	}
	cout << endl;
	//数据交换、查找、清理
	cout << "---------------------------" << endl;
	set<int> s2 = { 100,200,300 };
	s1.swap(s2);
	for (auto e : s1)
		cout << e << ' ';
	cout << endl;
	for (auto e : s2)
		cout << e << ' ';
	cout << endl;
	cout << "s2.find(8):";//
	cout << (s2.find(8) != s2.end()) << endl;//1
	cout << "s2.clear():" << endl;
	s2.clear();
	for (auto e : s2)
		cout << e << ' ';
	cout << endl;
	return 0;
}

打印结果如下:
在这里插入图片描述
最后,我们再谈一谈count,虽然count可以用来查找元素键值的数量的,但,对于set来说,由于不允许冗余的存在,因此count在这里实现的是查找元素是否存在。
实践代码如下:

#include <iostream>
#include <map>
#include <vector>
#include <set>
using namespace std;
int main()
{
	vector<int> arr1 = { 7,8,9,6,5,4,1,2,3 };
	set<int> s1(arr1.begin(), arr1.end());
	for (int i = 0; i < 10; i++)
	{
		if (s1.count(i))
			cout << i << "在set中" << endl;
		else
			cout << i << "不在set中" << endl;
	}
	return 0;
}

打印结果如下:
在这里插入图片描述
除了上述的使用方法之外,我们还可以通过更改比较方式来更改set中数值的排序方式。
如下:

int main()
{
		vector<int> arr1 = { 7,8,9,6,5,4,1,2,3 };
	set<int,greater<int>> s1(arr1.begin(), arr1.end());
	for (auto e : s1)
		cout << e << ' ';
	cout << endl;
	return 0;
}

结果如下:在这里插入图片描述
这样,我们就完成了降序排列元素。

四.set的特点

在这里插入图片描述
set具有以下特点:

  • 只有键值,键值就是实值,所以传递参数时只需要传递一个值。
  • 自带去重机制,不允许数据冗余
  • 使用迭代器遍历时,默认为升序
  • 普通迭代器也不允许修改。

五.multset

5.1multset的使用

multsetset的另一个版本,对于multset来说,不同点有二:

  • multset允许数据冗余
  • count可以在这里得到真正的使用。

我们先把CPP官网的图贴到下面:
在这里插入图片描述
这里,我们不再赘述multset的操作,先演示下数据冗余的效果。

int main()
{
  vector<int> arr1 = { 7,7,8,9,6,5,4,1,2,3 };
  multiset<int> s1(arr1.begin(), arr1.end());
  for (auto e : s1)
	  cout << e << ' ';
  cout << endl;
  return 0;
}

结果如下:

然后,我们现在再来使用以下count函数

 cout << s1.count(7) << endl;

结果:2

因此,在multset中,count函数可以计算出相同元素的数量。

5.2multset的查找以及结构

由于multset是允许冗余的,因此就出现了一个问题:如果我们要使用查找函数
那么,返回的是哪个元素呢?
我们来实验下:

int main()
{
  vector<int> arr1 = { 7,7,8,9,6,5,4,1,2,3 };
  multiset<int> s1(arr1.begin(), arr1.end());
  auto it = s1.begin();
  while (it != s1.end())
  {
	  cout << *it << ":" << &*it << endl;
	  it++;
  }
  cout << "s1.find(7):" << &*s1.find(7) << endl;
  return 0;
}

在这里插入图片描述
我们发现,我们查找到的是中序遍历中第一次出现的元素。
下面,我们看下multset的结构:
在这里插入图片描述

六.map

6.1map的介绍

map是二叉搜索树改造的key/value模型,是一个真正意义上的键值对模型。
应用场景很多,如下:

  • 中英文词典
  • 电话号码查询快递信息
  • 电话号码+验证码

首先,我们先来看以下map的文档介绍:
在这里插入图片描述
其中

  • 参数1:键值
  • 参数2:实值
  • 参数3:比较方式
  • 参数4:空间配置器

map中,会用到之前说过的pair结构,也就是说,first表示键值,second表示实值。

6.2map的定义方式

下面,我们来学习一下map的定义方式
在这里插入图片描述
通过上图,我们可以得知,可以有如下定义:
方式一: 指定key和value的类型构造一个空容器

map<int,string> ml;//键值为int,实值为string

方式二: 拷贝构造某类型容器

map<int,string> m2(m1);

方式三: 迭代器区间构造

map<int,string> m3(m2.begin(),m2.end());

方式四: 指定比较方式

map<int,string,greater<int>> m4;

我们下面实际的应用一下

int main()
{
	vector<pair<string, int>> arr = { make_pair("1",11),make_pair("2",22),make_pair("3",33) };
	map<string, int> m1;
	map<string, int> m2(arr.begin(),arr.end());
	cout << "m1:" << ' ';
	for (auto e : m1)
		cout << e.first << ':' << e.second << "  ";
	cout << endl;
	cout << "m2:" << ' ';
	for(auto e:m2)
		cout << e.first << ':' << e.second << "  ";
	cout << endl;
	return 0;
}

打印结果:

m1:
m2: 1:11 2:22 3:33

6.3map的使用

功能用途
迭代器遍历容器
empty判空
size当前容器元素数
max_size容器的最大容量
operator[]按照键值(key/first),访问实值(value/second)
insert指定位置插入
erase指定位置删除
swap交换两个容器
clear清空容器元素
find查找实值,并返回迭代器位置
count统计容器中指定键值(key/first)的数量

对于map而言,除了新增了一个operator[]和部分函数的返回值不一样之外,与set没啥区别。
下面,我们实践一下。

#include <iostream>
#include <map>
#include <vector>
#include <set>
using namespace std;
int main()
{
	vector<pair<string, int>> arr={ make_pair("kuku",1),make_pair("kiki",2) };
	map<string, int> m1(arr.begin(), arr.end());
	cout << "迭代器遍历结果:";
	map<string, int>::iterator it1 = m1.begin();
	while (it1 != m1.end())
	{
		cout << "<" << it1->first << ";" << it1->second << ">";//注意,这里用的是->操作符
		++it1;
	}
	cout << endl;
	//判空,解引用,size和maxsize
	cout << "empty:" << m1.empty() << endl;
	cout << "[]:" << m1["kuku"] << endl;//这里要通过键值访问
	cout << "size:" << m1.size() << endl;
	cout << "maxsize:" << m1.max_size() << endl;
	//插入
	m1.insert(make_pair("lili",3));
	cout << "insert:" <<(--m1.end())->first<< (--m1.end())->second<<endl;
	//删除
	m1.erase("lili");
	cout << "erase:";
	for (auto e : m1)
	{
		cout << "<" << e.first << ',' << e.second << ">,";
	}
	cout << endl;
	//交换,查找,清理
	map<string, int> m2 = {make_pair("trousers",44),make_pair("trou",66) ,make_pair("trouser",55)};
	m1.swap(m2);
	for (auto e : m1)
		cout << e.first << ' ' << e.second << ' ';
	cout << endl;
	for (auto e : m2)
		cout << e.first << ' ' << e.second << ' ';
	cout << endl;
	cout << (m1.find("kuku")!=m1.end()) << endl;//find返回的是一个迭代器
	cout << "m2.clear" << endl;
	m2.clear();
	cout<<m2.empty();
}

打印结果如下:
在这里插入图片描述
下面,我们详细的介绍几个函数

6.4 insert函数

由于insert函数要返回两个值:

  • 是否插入成功
  • 插入成功的迭代器位置

由于要返回两个值,因此insert的函数返回值应该是一个pair类型的。

pair<iterator,bool> insert(const value_type& val);

6.5 find和count函数

在map中,find和count都可以用来判断元素是否存在,但他们有以下的区别:

  • find返回的是迭代器
  • count返回的是键值数

6.6 map中的修改

map是可以修改pair中的第二个值的,也就是value。
我们可以通过迭代器进行修改,如下:

map<string,int>::iterator it1=m1.begin()++;
it1->second=77;

下面,我们可以用map来实现水果统计的代码:

vector<string> word={"苹果","梨子","桃子","苹果","梨子","桃子","苹果","梨子"};
map<string,int> table;
for(auto& e:word)
{
	if(!table.count(e))
		table.insert(make_pair(e,i));
	else
		table.find(e)->second++;
}
for(auto e:table)
{
	cout << "<" << e.first << ',' << e.second << ">,";
}

6.7 map中的operator[]

operator[]是返回实值,方括号内为键值。如果键值不存在的话,那么会插入信的键值对。
因此,operator是一个功能非常强大的东西。
我们可以结束operator[],把上面的统计水果改为下述代码:

int main()
{
	vector<string> word={"苹果","梨子","桃子","苹果","梨子","桃子","苹果","梨子"};
	map<string,int> table;
	for(auto& e:word)
	{
	table[e]++;
	}
	for(auto e:table)
	{
	cout << "<" << e.first << ',' << e.second << ">,";
	}
}

下面,我们介绍以下operator[]的实现:

  1. 调用insert,插入一个make_pair对象
  2. 获取到first,即获取到iterator
  3. 通过迭代器,获取到second

通过这样的一套形式,我们就可以实现一个operator
也就是说,是长这个样子的

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

因此,我们的operator[]是非常强大的,它有以下功能:插入+修改+查找。

七.map的特点

  • 包含键值和实值,插入时需要插入pair对象
  • 自带去重机制,不允许数据冗余(键值)
  • 使用迭代器遍历时,默认为升序(依靠键值排序)
  • 普通迭代器允许修改实值,const迭代器什么都不允许修改。

八.multimap

对于multimap而言,我们只需要注意两个点

  • 允许键值冗余
  • 没有重载operator[]

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

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

相关文章

goframe开发一个企业网站 模版界面4

###goframe已有了模板的功能 {{"string"}} // 一般 string {{raw string}} // 原始 string {{c}} // byte {{print nil}} // nil 也被支持 {{. | FuncA | FuncB | FuncC}}{{if .condition}}... {{else}}{{if .condition2}}...{{end}} {{end}}{{rang…

一、k8s快速入门之学习Kubernetes组件基础

一、三个容器管理器平台 Apache MESOS 开源的分布式资源管理框架&#xff0c;被推特选为基础平台&#xff0c;2019年推特换位k8s&#xff0c;MESOS最新版可以在MESOS上管理k8sDOCKER SWARM docker总部发行的&#xff0c;实现docker的集群方案&#xff0c;和docker捆版一起&…

初始JavaEE篇——多线程(7):定时器、CAS

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a;JavaEE 目录 定时器的使用 定时器的原理 模拟实现定时器 CAS 介绍 CAS的应用场景 解析 AtomicInteger 类 实现自旋锁 CAS的缺陷…

【UGUI】为射击游戏添加动态显示的分数和血量到UI界面

项目背景 在这个项目中&#xff0c;我们希望实现一个简单的游戏系统&#xff0c;其中玩家可以通过击中目标来获得分数&#xff0c;同时通过与怪物碰撞来减少血量。分数和血量需要在游戏界面上实时显示&#xff0c;以便玩家能够随时了解自己的状态。 技术实现 1. 静态变量的使…

stm32引脚PB3、PB4、PA15作为普通IO口用时,需要先解除调试端口复用

当项目调试的时候&#xff0c;发现PA15引脚无论配置输出高还是低或者输入&#xff0c;均只能输出3.3V的高电平。 目前STM的硬件调试有两种方法&#xff0c;JTAG和SW的方式&#xff0c;目前个人认为最好的方式就是SW&#xff0c;因为它只占用PA13和PA14两个IO。而JTAG还要多占用…

MATLAB与STK互联:仿真并获取低轨卫星与指定区域地面站的可见性数据

MATLAB控制STK实现&#xff1a;仿真并获取低轨卫星与指定区域地面站的可见性数据 本次仿真主要参考了多篇文献和网站&#xff0c;包括但不限于&#xff1a;《Using MATLAB for STK Automation》、CSDN博文&#xff1a; 拜火先知的博客_CSDN博客-笔记、AGI官网有关MATLAB的内容…

用Python设置、更新和获取Excel单元格的值

Excel工作簿作为一款广泛使用的数据管理工具&#xff0c;与Python相结合&#xff0c;可以使得自动化处理大量数据成为可能。通过Python来设置、更新以及读取Excel单元格的值&#xff0c;不仅可以极大地提高工作效率&#xff0c;减少重复劳动&#xff0c;还能增强数据处理流程的…

Golang | Leetcode Golang题解之第525题连续数组

题目&#xff1a; 题解&#xff1a; func findMaxLength(nums []int) (maxLength int) {mp : map[int]int{0: -1}counter : 0for i, num : range nums {if num 1 {counter} else {counter--}if prevIndex, has : mp[counter]; has {maxLength max(maxLength, i-prevIndex)} …

提升网站安全性 HTTPS的重要性与应用指南

内容概要 在如今数字化快速发展的时代&#xff0c;网站安全显得尤为重要。许多用户在访问网站时&#xff0c;尤其是涉及个人信息或金融交易时&#xff0c;对数据传输的安全性有着高度的关注。HTTPS&#xff08;超文本传输安全协议&#xff09;正是为了满足这种需求而诞生的。通…

DICOM标准:解析DICOM属性中的病人模块

目录 病人模块概述 1. 病人关系模块&#xff08;Patient Relationship Module&#xff09; 2. 病人识别模块&#xff08;Patient Identification Module&#xff09; 3. 病人统计模块&#xff08;Patient Demographic Module&#xff09; 4. 病人医学模块&#xff08;Pati…

编写高性能爬虫抓取股票行情数据

最近给一个私募大佬帮忙做了一些股票交易有关的系统&#xff0c;其中涉及到行情数据抓取的问题&#xff0c;一番摸索之后&#xff0c;把成果在这里做个分享。 我把行情抓取的部分&#xff0c;和一个写手记的小功能&#xff0c;单独拿了出来放在一个小系统里面&#xff0c;可以…

人像摄影笔记(自用)

相机的原理&#xff1a;镜头--CMOS传感器---通过ISP的计算 然后通过手机的GPU处理后呈现出图片的形式 镜头&#xff1a;定焦和变焦&#xff0c;变焦分为光学变焦和数字变焦 光学变焦&#xff1a;焦距变了 画质不变 数字变焦&#xff1a;焦距不变 裁剪画质 数字变焦一…

前端埋点与监控最佳实践:从基础到全流程实现.

前端埋点与监控最佳实践&#xff1a;从基础到全流程实现 大纲 我们会从以下三个方向来讲解埋点与监控的知识&#xff1a; 什么是埋点&#xff1f;什么是监控&#xff1f; JS 中实现监控的核心方案 写一个“相对”完整的监控实例 一、什么是埋点&#xff1f;什么是监控&am…

电能质量治理产品在分布式光伏电站的应用

1.概述 随着全球对可再生能源需求的不断增长&#xff0c;分布式光伏电站的建设与扩张正迅速发展。然而&#xff0c;在其运行过程中&#xff0c;分布式光伏电站遭遇了一系列挑战&#xff0c;包括企业关口计量点功率因数降低和谐波污染等问题。这些问题不仅影响了光伏电站的运行…

遥感图像Trento原始数据集下载

遥感图像Trento原始数据集下载 偶然间在某个项目里发现了Trento的完整数据集&#xff0c;不过那个数据集有些奇怪的小改动 虽然我已经不做遥感方向了&#xff0c;不过当初我找这个数据集也是花了很长时间 于是重新整理了一下&#xff0c;就当是方便后来的研究者使用吧 githu…

GenAI 生态系统现状:不止大语言模型和向量数据库

自 20 个月前 ChatGPT 革命性的推出以来&#xff0c;生成式人工智能&#xff08;GenAI&#xff09;领域经历了显著的发展和创新。最初&#xff0c;大语言模型&#xff08;LLMs&#xff09;和向量数据库吸引了最多的关注。然而&#xff0c;GenAI 生态系统远不止这两个部分&#…

个人应用接入使用阿里云盘和百度网盘

一、阿里云盘 官方文档接入流程 语雀流程概述服务端 API 调用流程如下图所示1. 创建账...https://www.yuque.com/aliyundrive/zpfszx/btw0tw 1. 接入授权 1.1. App Key、App Secret和用户授权验证 在通过网盘开发者认证之后&#xff0c;创建个人应用会生成APP ID&#xff…

医院信息化与智能化系统(15)

医院信息化与智能化系统(15) 这里只描述对应过程&#xff0c;和可能遇到的问题及解决办法以及对应的参考链接&#xff0c;并不会直接每一步详细配置 如果你想通过文字描述或代码画流程图&#xff0c;可以试试PlantUML&#xff0c;告诉GPT你的文件结构&#xff0c;让他给你对应…

从比亚迪超越特斯拉,看颠覆全球市场的中国力量

这是比亚迪CEO王传福早年在日本调研电池供应链时发出的感慨。 那时的人们谁也没有想到&#xff0c;比亚迪会从深圳的一家普通的电池供应商开始做起&#xff0c;拼出一条属于自己的“血路”&#xff0c;摇身一变成为名副其实的“电车之王”&#xff0c;并让全球车企仰望。 比亚…

3d 添加辅助坐标器和轨道控制器

1.添加辅助坐标器 使用AxesHelper类来添加坐标轴辅助器&#xff0c;辅助器简单模拟3个坐标轴的对象。红色代表X轴&#xff0c;绿色代表Y轴&#xff0c;蓝色代表Z轴。 // 创建坐标轴辅助器&#xff0c;5是坐标轴的长度 const axesHelper new THREE.AxesHelper(5); // 将坐标轴…