C++map和set

news2024/11/26 14:24:56

目录:

  • 什么是关联式容器?
    • 键值对
      • 树形结构的关联式容器
  • set的概念
      • multiset的使用
          • pair和make_pair
        • map的概念
            • 用“[]”实现统计水果的次数
          • multimap的使用

什么是关联式容器?

在初阶阶段,我们已经接触过STL中的部分容器,比如:vector、list、deque、forward_list(C++11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。那关联式容器又是什么呢?其实关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高。

键值对

用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。比如:现在要建立一个英汉互译的字典,那该字典中必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应该单词,在词典中就可以找到与其对应的中文含义。

树形结构的关联式容器

根据应用场景的不同,STL总共实现了两种不同结构的管理式容器:树型结构与哈希结构。树型结构的关联式容器主要有四种:map、set、multimap、multiset。这四种容器的共同点是:使用平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列。

set的概念

定义: set是关联容器,也就是按照一定次序存储元素的容器

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

我们可以通过cplusplus网站查到set的一些成员函数:
网站点击进入
在这里插入图片描述
代码实现一个set:

#include<iostream>
#include<set>
using namespace std;

void test_set1()
{
	set<int>  s;
	s.insert(3);
	s.insert(1);

	s.insert(4);

	s.insert(7);

	s.insert(2);

	s.insert(1);
	//排序+去重
	auto it = s.begin();//关联式容器的迭代器 it
	//set<int>::iterator it =s.begin();
	while (it != s.end())
	{
		cout << *it << " ";
		++it;
	}
	  cout << endl;

	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;
	//auto pos = s.find(3);
	auto pos = find(s.begin(), s.end(), 3);
	//算法里的find也能查找,因为find写的是一个模板,底层是迭代器实现的,但是效率不高
	if (pos != s.end())
	{
		s.erase(pos);//找到pos删除
	}
	//s.erase(1) << endl;//给值删除
	cout << s.erase(1) << endl;//对于基于值的版本,该函数返回擦除的元素数量   
	cout << s.erase(3) << endl;//对于基于值的版本,该函数返回擦除的元素数量  
	for (auto e : s)
	{
		cout << e << " ";
	} 
	cout << endl;	
}
int main()
{

	test_set1();

	return 0;
}

输出结果:
在这里插入图片描述
第一行结果:通过运行我们看到第一行的输出结果是按照升序排序并且数据不重复,我们插入(insert)的是两个1,输出一个1,说明set底层按照二叉搜索树走中序进行了排序并且还去重。
第二行结果:为了方便我们也可以用范围for来输出结果,因为范围for的底层也是迭代器实现的。
erase删除元素的时候,函数会返回擦除的元素数量,0表示找到pos位置的元素并删除了,1表示pos没有去找还在,元素是直接删除的。
在这里插入图片描述

总结:

  1. 与map/multimap不同,map/multimap中存储的是真正的键值对<key, value>,set中只放value,但在底层实际存放的是由<value, value>构成的键值对。
  2. set中插入元素时,只需要插入value即可,不需要构造键值对。
  3. set中的元素不可以重复(因此可以使用set进行去重)。
  4. 使用set的迭代器遍历set中的元素,可以得到有序序列
  5. set中的元素默认按照小于来比较
  6. set中查找某个元素,时间复杂度为:(O)logN,也就是说查找一千个元素找10次、一百万个元素找20次、10亿个元素才找30次左右
  7. set中的元素不允许修改(为什么?)
  8. set中的底层使用二叉搜索树(红黑树)来实现的,并且左右两边比较均衡,因为二叉搜索树有退化成单支的情况,所以准确来说底层是平衡二叉搜索树来实现的

multiset的使用

定义: multiset是按照特定顺序存储元素的容器,其中元素是可以重复的。
代码实现:

void test_set2()
{
	multiset<int>  s;//multiset允许冗余,排序

	s.insert(3);
	s.insert(3);
	s.insert(3);

	s.insert(1);

	s.insert(4);
	s.insert(3);

	s.insert(7);
	s.insert(3);

	s.insert(2);

	s.insert(1);
	auto it = s.begin();//关联式容器的迭代器 it
//set<int>::iterator it =s.begin();
	while (it != s.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;
	auto pos = s.find(3);
	//multiset从第一个3开始查找
	while (pos != s.end())
	{
		cout << *pos << " ";
		++pos;
	}
	cout << endl;
}

在这里插入图片描述
从上面的运行结果我们不难看出multiset支持键值冗余,可以排序,我们用find查找的时候只需要找到第一个3往后面走(pos++)就能找到所有的3。

总结:

  1. multiset中再底层中存储的是<value, value>的键值对
  2. mtltiset的插入接口中只需要插入即可
  3. 与set的区别是,multiset中的元素可以重复,set是中value是唯一的
  4. 使用迭代器对multiset中的元素进行遍历,可以得到有序的序列
  5. multiset中的元素不能修改
  6. 在multiset中找某个元素,时间复杂度为:O(logN)
  7. multiset的作用:可以对元素进行排序
    lower_bound、upper_bound和equal_range的使用:

lower_bound(val):返回一个指向当前 set 容器中第一个大于或等于 val 的元素的双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
upper_bound(val):返回一个指向当前 set 容器中第一个大于 val 的元素的迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
equal_range:返回一个区域的边界,该区域包含容器中等效于 val 的所有元素。由于 set 容器中的所有元素都是唯一的,因此返回的范围最多包含一个元素。

#include<iostream>
using namespace std;
#include<set>
int main()
{
	set<int> myset;//定义一个名为myset的关联式容器
	set<int>::iterator itlow, itup;

	for (int i = 1; i < 10; i++)
		myset.insert(i * 10);
	//itlow = myset.lower_bound(30);
	itlow = myset.lower_bound(35);//>=


	itup = myset.upper_bound(60);//>

	myset.erase(itlow, itup);//迭代给的区间是左闭右开

	cout << "myset contains:";

	for (set<int>::iterator it = myset.begin(); it != myset.end(); ++it)//查询或者遍历set中的元素时,可以用到iterator迭代器
	
		cout << ' ' << *it;
 	    cout << '\n';
	return 0;
}

在这里插入图片描述
我们给的是下限val是35,上限是val60,那么会删除[40-70)的值域,区间是左闭右开,也就是删除40、50和60。

#include <set>
#include<iostream>
using namespace std;
int main()
{
	std::set<int> myset;

	for (int i = 1; i <= 5; i++) myset.insert(i * 10);   // myset: 10 20 30 40 50

	std::pair<std::set<int>::const_iterator, std::set<int>::const_iterator> ret;
	ret = myset.equal_range(30);

	std::cout << "the lower bound points to: " << *ret.first << '\n';
	std::cout << "the upper bound points to: " << *ret.second << '\n';

	return 0;
}

在这里插入图片描述
这里我们用到了pair,下面会讲pair的用法,该函数返回一对,其成员first是范围的下限(与 lower_bound 相同),second 是上限(与 upper_bound 相同)。因此,equal_range只需要设定一个val就可以了,那么这个set也可以说是指向元素的双向迭代器类型。

pair和make_pair

pair:此类将一对值耦合在一起,这些值可能属于不同类型的:因为pair的底层是struct实现的,不是class,所以可以直接使用pair的成员变量first和second ,也就是说当一个函数需要返回2个数据的时候,这2个数据可以是不同类型,可以选择pair。
简单实现一下:

#include<iostream>
#include<set>
using namespace std;
int main()
{


	//pair对象
	pair<int, double> p1;

	p1.first = 1;

	p1.second = 2.5;

	cout << p1.first << ' ' << p1.second << endl;
	
	return 0;

}

在这里插入图片描述

make_pair:模板类型可以从传递给的参数隐式推导,如果包含不同类型的其他对象是隐式可转换的,则可以从包含不同类型的其他对象构造对象(利用make_pair创建新的pair对象)
简单实现一下:

#include<iostream>
#include<set>
using namespace std;
int main()
{
	pair<int, double> p1;

	p1 = make_pair(1, 1.5);

	cout << p1.first << " " << p1.second << endl;

	int a = 10;

	string m = "loquot";

	pair<int, string> newObj;

	newObj = make_pair(a, m);
	
	cout << newObj.first << ' ' << newObj.second << endl;
	system("pause");
	
	return 0;
}

在这里插入图片描述

map的概念

定义:map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素。
实现map并统计出水果的次数:

#include<iostream>
#include<map>
#include<set>
#include<string>
using namespace std;
int main()
{
	map<string, string> dict;//定义一个字典
	dict.insert(pair<string, string>("排序", "sort"));
	dict.insert(pair<string, string>("左边", "left"));
	dict.insert(pair<string, string>("右边", "right"));
	dict.insert(make_pair("字符串", "string"));
	dict["迭代器"] = "iterator";//插入+修改
	dict["insert"];//插入
	dict.insert(pair<string, string>("左边", "***"));//插入失败,搜索树只compare key
	dict["insert"] = "插入";//修改
	cout << dict["左边"] << endl;//查找   



	//key在就是查找,不在就是插入
	//map<string, string>::iterator it = dict.begin();
	dict.insert(make_pair("字符串", "string"));
	auto it = dict.begin();
	//while (it != dict.end()) 
	//{ 
	//	//cout<<(*it).first <<" "<<(*it).second<< endl;
	//	//pair不支持流插入,struct类型没有访问限定符,可以直接获取it的元素
	//	cout << it->first<<":" << it->second << endl;
	//	//类型是结构体的时候,第一个箭头返回的是数据的指针,第二个箭头访问这个值,两个箭头不好看,编译器做了处理省略了一个箭头
	//	++it;
	//} 
	//统计次数
	for (const auto& kv : dict)
	{
		//dict的每一个值是pair,pair里的每一个值是string,不加引用就是string的拷贝构造,代价太大
		cout << kv.first << ":" << kv.second << endl;//*it的元素赋值给了kv
	}
	string arr[] = { "苹果","西瓜","香蕉","草莓","苹果","西瓜","苹果","苹果","西瓜","苹果","香蕉","苹果","香蕉" };

	map<string, int> countMap;
	//map<string,int>::iterator it=countMap;
//
	for (auto& e : arr)
	{

		auto it = countMap.find(e);//查找水果
		if (it == countMap.end())//没有
		{
			countMap.insert(make_pair(e, 1));
		}
		else  
		{
			it->second++;//次数加加
		}
	}
	
	for (const auto& kv : countMap)
	{
		//dict的每一个值是pair,pair里的每一个值是string,不加引用就是string的拷贝构造,代价太大
		cout << kv.first << ":" << kv.second << endl;//*it的元素赋值给了kv
	}
	return 0;
	
	}

在这里插入图片描述
map中方括号[]的功能有三点:
1.插入
2.修改
3.查找

用“[]”实现统计水果的次数
#include<iostream>
#include<map>
#include<set>
#include<string>
using namespace std;
int main()
{
	map<string, string> dict;//定义一个字典
	auto it = dict.begin();
	//统计次数
	for (const auto& kv : dict)
	{
		//dict的每一个值是pair,pair里的每一个值是string,不加引用就是string的拷贝构造,代价太大
		cout << kv.first << ":" << kv.second << endl;//*it的元素赋值给了kv
	}
	string arr[] = { "苹果","西瓜","香蕉","草莓","苹果","西瓜","苹果","苹果","西瓜","苹果","香蕉","苹果","香蕉" };

	map<string, int> countMap;
//用方括号也能实现统计次数
for (auto& e : arr)
{
	countMap[e]++;
}
for (const auto& kv : countMap)
{
		cout << kv.first << ":" << kv.second << endl;//*it的元素赋值给了kv

}
	cout << endl;
	return 0;
}

在这里插入图片描述

总结:

  1. map中的的元素是键值对
  2. map中的key是唯一的,并且不能修改
  3. 默认按照小于的方式对key进行比较
  4. map中的元素如果用迭代器去遍历,可以得到一个有序的序列
  5. map的底层为平衡搜索树(红黑树),查找效率比较高 O ( l o g 2 N ) O(log_2 N) O(log2N)
  6. 支持[]操作符,operator[]中实际进行插入查找
multimap的使用

定义:multimap是多重映射是关联容器,用于存储由键值和映射值的组合形成的元素,遵循特定顺序,并且多个元素可以具有等效的键,和multiset一样支持冗余。


#include<iostream>
#include<map>
#include<set>
#include<string>
using namespace std;
int main()
{
	multimap<string, string> dict;
	dict.insert(make_pair("left", "左边"));
	dict.insert(make_pair("left", "剩余"));
	dict.insert(make_pair("string", "字符串"));

	dict.insert(make_pair("left", "xxx"));
	for (const auto& kv :dict)
	{
		cout << kv.first << ":" << kv.second << endl; 
	}


	string arr[] = { "苹果","西瓜","香蕉","草莓","苹果","西瓜","苹果","苹果","西瓜","苹果","香蕉","苹果","香蕉" };

	multimap<string, int> countMap;
	//map<string,int>::iterator it=countMap;

	for (auto& e : arr)
	{

		auto it = countMap.find(e);//查找水果
		if (it == countMap.end())//没有
		{
			countMap.insert(make_pair(e, 1));
		}
		else
		{
			it->second++;//次数加加
		}
	}
		for (const auto& kv : countMap)
	{
		cout << kv.first << ":" << kv.second << endl;//*it的元素赋值给了kv
	}

	cout << endl;

	system("pause");

	return 0;
	
}

在这里插入图片描述
通过结果我们看到使用multimap也能很好的统计出水果出现的次数,因为muitmap支持键值冗余,我们使用的成员函数find会先查找此类水果有没有出现,有就加加次数不会再反复insert,没有我没再insert然后再加加次数。

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

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

相关文章

Centos7 Failed to start login service 问题

最近发现Centos7有个问题&#xff0c;用普通用户登录的时候&#xff0c;打开命令窗口无法进行操作一直卡在那里&#xff0c;但切换到root用户后命令输入又正常。因为我需要从 window 上的 SecureCRT 去连接 Centos7&#xff0c;每次都需要用户登录&#xff0c;然后把防火墙关闭…

TLD7002学习笔记(一)-芯片介绍

文章目录 1. 前言2. TLD7002-16ES简介3. TLD7002-16ES基本功能介绍3.1 引脚和功能框图3.2 状态机3.2.1 正常工作时状态机3.2.2 OTP烧录或者仿真时的状态机 3.3 GPIN0和GPIN1引脚3.4 器件地址3.5 电流配置3.6 PWM配置3.7 并行输出&热降额&热过载保护 TLD7002-16ES诊断功…

高完整性系统:INTRODUCING ADA

目录 1. ADA的历史 2. ADA的特点 2.1 Strong, Static Typing 强语言、强静态类型语言 2.1.1 ADA is Strong, Static Typing 2.1.2 C is Weak, Static Typing 2.2 Module System 2.3 Portable 2.3.1 ADA 2.3.2 C 2.3.3 Cost of Runtime Checking 2.4 Readability …

Java jvm调优

系列文章目录 文章目录 系列文章目录前言JVM 基础面试题11. JDK&#xff0c;JRE以及JVM的关系2. 我们的编译器到底干了什么事&#xff1f;3. 类加载机制是什么&#xff1f;3.1 装载(Load)3.2 链接(Link)验证(Verify)准备(Prepare)解析(Resolve) 3.3 初始化(Initialize) 4. 类加…

chatgpt赋能python:Python三次幂与其应用

Python三次幂与其应用 Python是一种高级编程语言&#xff0c;因其简单易用的语法和广泛应用而备受欢迎。在该语言中&#xff0c;三次幂是其中一个常用操作之一。本文将介绍Python三次幂的概念及其应用&#xff0c;为您带来有价值的参考。 什么是Python三次幂&#xff1f; Py…

KubeSphere 社区双周报 | 杭州 Meetup 报名中 | 2023.05.12-05.25

KubeSphere 社区双周报主要整理展示新增的贡献者名单和证书、新增的讲师证书以及两周内提交过 commit 的贡献者&#xff0c;并对近期重要的 PR 进行解析&#xff0c;同时还包含了线上/线下活动和布道推广等一系列社区动态。 本次双周报涵盖时间为&#xff1a;2023.05.12-2023.…

Linux - Java 8 入门安装与重装教程集锦

一、入门初始安装 1. 具体安装教程 1. linux 系统中如何安装java环境&#xff08;通过tar.gz文件&#xff09; 安装包下载链接 Java 的 tar.gz 安装包下载链接传送门 Linux 系统的 Java 环境变量配置教程 1. linux查看java版本&#xff0c;以及配置java home 2. Linux环…

stackqueue

这篇主要讲栈(stack)和队列(queue)&#xff0c;实际要学习的数据结构有三个&#xff1a;stack、queue、priority_queue 这些数据结构已经不属于容器了&#xff0c;而是容器适配器。 list的第二个参数是空间配置器&#xff0c;支持申请空间&#xff1b;而list和queue的第二个参…

Windows下利用Anaconda创建多个CUDA环境

参考 https://blog.csdn.net/qq_42395917/article/details/126237388 https://blog.csdn.net/qq_42406643/article/details/109545766 (待学习补充) https://blog.csdn.net/qq_43919533/article/details/125694437 (待学习补充) 安装cudatoolkit和cudnn # 前提是我已经安装了…

【Python 打包应用发布程序】零基础也能轻松掌握的学习路线与参考资料

Python是一种流行的编程语言&#xff0c;因其易学易用、灵活和高效而受到广泛关注和应用&#xff0c;尤其是在开发Web应用、数据科学和人工智能方面。Python的强大之处在于其丰富的第三方库和工具&#xff0c;可以让开发者轻松地构建复杂的应用程序和脚本工具。但是&#xff0c…

完全掌握git入门到精通各类免费书籍整理

大型软件项目开发&#xff0c;多人群组开发都离不开的版本控制工具 git&#xff0c;命令简单&#xff0c;想要完全掌握却需要付出一点时间。我们将一些评价较高的git免费学习资料网站做了整理&#xff0c;收录到 学习使用git完全指南各种免费书籍分享 完全掌握git入门到精通各…

【IDEA插件开发】快速入门系列01 开发一个简单的Idea插件

IDEA插件开发流程 IDEA插件开发官方文档 英文好的建议阅读官方文档 IDEA插件开发官方文档&#xff1a;https://plugins.jetbrains.com/docs/intellij/welcome.html 搭建IDEA插件开发环境 1.安装社区版IDEA 在这里我们需要下载IDEA社区版的历史版本。 历史版本的下载网址&a…

自学黑客?一般人我劝你还是算了吧

博主本人 18年就读于一所普通的本科学校&#xff0c;21年 6 月在三年经验的时候顺利通过校招实习面试进入大厂&#xff0c;现就职于某大厂安全联合实验室。 我为啥说自学黑客&网络安全&#xff0c;一般人我还是劝你算了吧。因为我就是那个不一般的人。 首先我谈下对黑客&am…

Java日期时间调整的几种方式

一、Calendar类 我们现在已经能够格式化并创建一个日期对象了&#xff0c;但是我们如何才能设置和获取日期数据的特定部分呢&#xff0c;比如说小时&#xff0c;日&#xff0c;或者分钟? 我们又如何在日期的这些部分加上或者减去值呢? 答案是使用Calendar 类。 Calendar类的…

六、基本数据类型

数据类型 一、基本数据类型 以下是go中可用的基本数据类型 1.1 布尔型bool 布尔型的值只可以是常量 true 或者 false。一个简单的例子&#xff1a;var b bool true 1.2 数值型 1、整数型 int8 有符号 8 位整型 (-128 到 127) 长度&#xff1a;8bit int16 有符号 16 位整…

jmeter接口工具使用详解之基础介绍

目录 一、优点 二、安装及下载 三、基础构成 jmeter是一款优秀的开源性能测试工具&#xff0c; 一、优点 1、开源工具&#xff0c;可扩展性非常好 2、高可扩展性&#xff0c;用户可自定义调试相关模块代码 3、精心简单的GUI设计&#xff0c;小巧灵活 4、完全的可移植性…

电子科技大学编译原理复习笔记(五):词法分析

目录 前言 重点一览 词法分析概述 词法分析的功能 词法分析器的输出形式 词法分析器的结构 状态转换图 状态转换图的构造 词法分析器的设计 基本结构 内容 符号表 目的 组成 在词法分析中的作用 符号表的一般形式 常用的符号表结构 总结与补充 为何分离词法…

小学百分数思维导图怎么画?这样制作不出错

百分数是数学中的一个重要概念&#xff0c;它用于表示一个数在另一个数中所占的比例。在日常生活中&#xff0c;我们经常使用百分数进行计算和比较。而百分数思维导图是一种图形化的表示方法&#xff0c;用于展示数值之间的比例关系。它通过将数值转化为百分数的形式&#xff0…

python学习——描述统计df.describe

文章目录 1 描述统计1.1 查看常见统计量 describe1.2 一般对数值型数据统计1.2.1 基于非空数值统计sum\mean\max\min\var\std1.2.2 每一列中最大值的行索引 idxmax1.2.3 每一行中最大值的列索引 idxmax&#xff08;axis 1&#xff09;1.2.4 非空的数量 count() 1.3 一般对字符…

Sugar BI 预测服务:快速通过机器学习,进行数据预测分析

什么是预测服务&#xff1f; 机器学习是一门关于数据学习的科学技术&#xff0c;它能帮助机器从现有的复杂数据中学习规律&#xff0c;以预测未来的行为结果和趋势。 Sugar BI 作为对数据进行分析的可视化平台&#xff0c;也支持用户对自己的数据使用机器学习算法进行探索试分…