【C++】set与map

目录

一、预备知识:

1、关联式容器:

2、键值对:

3、树形结构的关联式容器:

二、set:

        1、set的介绍:

2、使用:

1、set的构造:

2、set的各种功能:

3、multiset

三、map:

1、map的介绍:

2、使用:

        1、map的构造:

2、map的各种功能:

3、map的operator[ ]

3、multimap:


一、预备知识:

1、关联式容器:

在以前阶段,已经接触过STL中的部分容器,比如:vector、list、deque等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。

那什么是关联式容器?它与序列式容器有什么区别?

 
关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key, value>结构的键值对,<key,value> 结构就是根据键值key以某种特定的方式来存放的,而value则是通过寻找key所在的位置,就可以找到value了。

比如在下面中,就是一个<key, value>结构,可以通过中文(key)来找到每一个对应的英文(value),也可以说这些value是通过每个对应的key找到的。

2、键值对:

用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。

如上所示:现在要建立一个英汉互译的字典,那该字典中必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应该单词,在词典中就可以找到与其对应的中文含义。

在标准库中提供了类似下面pair这种结构体来进行操作:

pair作用是将两个普通元素first和second(可以是 C++ 基本数据类型、结构体、类自定的类型)创建成一个新元素<first, second>。

#include<iostream>
using namespace std;
namespace ppr
{//SGI 版 STL 中的实现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){}};
}
int main()
{pair<string, int> p1("hehe", 888);ppr::pair<int, string> p2(999, "hehe");cout << "p1:" << p1.first << " " << p1.second << endl;cout << "p2:" << p2.first << " " << p2.second << endl;return 0;
}

pair中的first表示键值,second则表示实值,在给关联式容器中插入数据时,可以构建pair对象,毕竟存在模版参数,所以自己来控制pair的两个类型,就可以通过first来找到second。

比如上面就构建了一个键值key为string,实值value为int的键值对p1和键值key为int,实值value为string的键值对p2,其中pair中的first是key,second是value。

除了以上这种方法,在库中设了一个函数模版make_pair,可以自主传参,从而去构建对象

make_pair在库中的实现如下:

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

编译器会将这个函数转化成内联函数,这样话就节省了很多时间,但也不会造成过多的空间消耗。

3、树形结构的关联式容器:

根据应用场景的不同,STL总共实现了两种不同结构的管理式容器:树型结构与哈希结构。

树型结构的关联式容器主要有四种:map、set、multimap、multiset。这四种容器的共同点是:使用平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列。

C++ 11 还新增了4种哈希容器:unordered_map,unordered_multimap,unordered_set,unordered_multiset。但是哈希容器底层采用的是哈希表,而不是红黑树。

不同的是哈希结构的关联式容器比给予树形结构的关联式容器,提高添加和删除元素的速度以及提高查找算法的效率。

二、set:

        1、set的介绍:

set是按照一定次序存储元素的容器,它是key结构的,只包含一个键值,如上的官方文档中,T就是set的键值类型,Compare是根据后面参数判断是升序还是降序,Alloc是空间配置器。

理解:

set中的元素不可以重复(因此可以使用set进行去重),使用set的迭代器遍历set中的元素,可以得到有序序列。

2、使用:

1、set的构造:

int main()
{set<int> s1;cout << "构造空的set:s1" << endl;set<int>::iterator it1 = s1.begin();while (it1 != s1.end()){cout << *it1 << " ";it1++;}cout << endl;int arr[] = { 5,2,2,1,5,6,3,2,7,8 };set<int> s2(arr, arr + sizeof(arr)/sizeof(arr[0]));cout << "用区间元素构造set:s2" << endl;set<int>::iterator it2 = s2.begin();while (it2 != s2.end()){cout << *it2 << " ";it2++;}cout << endl;cout << "set的拷贝构造:s3" << endl;set<int> s3(s2);set<int>::iterator it3 = s3.begin();while (it3 != s3.end()){cout << *it3 << " ";it3++;}cout << endl;return 0;
}

通过以上代码可以看到,set构造后通过迭代器遍历会去重和排序,去重实质上是如果在set中存在就不会insert进去。

2、set的各种功能:

功能样例:

int main()
{vector<int> arr1 = { 1,2,3,4,6,7,8,9,0 };set<int> s1(arr1.begin(), arr1.end());cout << "迭代器遍历容器" << endl;set<int>::iterator it1 = s1.begin();while (it1 != s1.end()){cout << *it1 << " ";it1++;}cout << endl;cout << "insert 元素:" << endl;s1.insert(99);s1.insert(5);s1.insert(-1);for (const auto& e : s1)cout << e << " ";cout << endl;cout << "erase 元素:" << endl;s1.erase(5);s1.erase(7);s1.erase(-1);for (const auto& e : s1)cout << e << " ";cout << endl;vector<int> arr2 = { 11, 22, 88, 33, 55, 44, 99 };set<int> s2(arr2.begin(), arr2.end());cout << "s2遍历:" << endl;for (const auto& e : s2)cout << e << " ";cout << endl;cout << "s1 和 s2交换后:" << endl;s1.swap(s2);cout << "s1:" << endl;for (const auto& e : s1)cout << e << " ";cout << endl;cout << "s2:" << endl;for (const auto& e : s2)cout << e << " ";cout << endl;cout << "s1.find(44): " << endl;cout << (s1.find(44) != s1.end()) << endl;set<int> s3(s1.find(44), s1.end());//找到后返回当前位置的迭代器cout << "s3:" << endl;for (const auto& e : s3)cout << e << " ";cout << endl;cout << "s1.find(4): " ;cout << (s1.find(4) != s1.end()) << endl;cout << "s1的size:"  << s1.size() << endl;cout << "s2的size:"  << s2.size() << endl;cout << "s3的size:" << s3.size() << endl << endl;//count是计算容器中指定元素的个数的,但是set的元素不能有多个,所以count也就只能统计在不在vector<int> arr = {1,3,5,6,7,8,0};set<int> s5(arr.begin(), arr.end());for (int i  = 0;i<10;i++){if (s5.count(i))cout << i << " 在 s5 中" << endl;elsecout << i << " 不在 s5 中" << endl;}return 0;
}

在set中,默认是升序的,但是可以改变第二个模版参数Compare来进行控制升降序

int main()
{vector<int> arr3 = { 11,22,33,44,55,66,77,88,99,100 };set<int> s6(arr3.begin(), arr3.end());set<int, greater<int>> s7(arr3.begin(), arr3.end());cout << "s6:";for (const auto& e : s6)cout << e << " ";cout << endl;cout << "s7:";for (const auto& e : s7)cout << e << " ";cout << endl;return 0;
}

3、multiset

multiset 与 set 的区别:set支持唯一键值,每个元素值只能出现一次;而 multiset 中同一值可以出现多次。

其余操作基本是相同的。

vector<int> arr = { 1,2,2,2,2,3,3,3,4,5,6,7,8,9,9 };
multiset<int> ms(arr.begin(), arr.end());
for (auto e : ms)cout << e << " ";
cout << endl;

注意:

在查找后返回的是第一个的迭代器,并且count计数也在这用。

int main()
{vector<int> arr = { 1,2,2,2,2,3,3,3,4,5,6,7,8,9,9 };multiset<int> ms(arr.begin(), arr.end());for (auto e : ms)cout << e << " ";cout << endl;cout << "查找3,返回的是第一个出现的3的迭代器" << endl;multiset<int>::iterator it = ms.find(3);while (it != ms.end()){cout << *it << " ";it++;}cout << endl;cout << "count在这也很好用" << endl;cout << "计数2 : " << ms.count(2) << endl;cout << "计数3 : " << ms.count(3) << endl;return 0;
}

三、map:

1、map的介绍:

1、map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素。

2、在map中,键值key通常用于排序惟一的标识元素,而值value中存储与此键值key关联的 内容。

3、键值key和值value的类型可以不同,并且在map的内部,key与value通过成员类型 value_type绑定在一起,为其取别名称为pair。

比如:

在英汉对应的词典中就用到了这种KV模型

如上所示文档中,Key就是键值对中的键值,T则是键值对中的实值,在 map 中会用到前面提到过的pair结构,其中 first 表示键值,second 表示实值。

 在访问map中的键值和实值时,需要通过pair对象指定访问,比如m.first

2、使用:

        1、map的构造:

int main()
{map<int, int> m1;vector<pair<int, string>> arr = { make_pair(1,"one"),make_pair(2,"two"),make_pair(3,"three"),make_pair(4,"four"),make_pair(5,"five"),make_pair(6,"six") };map<int, string> m2(arr.begin(), arr.end());for (auto& e : m2){cout << e.first << " " << e.second << endl;}cout << endl;cout << "m3拷贝构造m2" << endl;map<int, string> m3(m2);for (auto& e : m3){cout << e.first << " " << e.second << endl;}cout << endl;return 0;
}

2、map的各种功能:

总的来说和set大差不差,主要是多了operator[ ]

功能样例:

int main()
{vector<pair<int, string>> arr1 = { make_pair(1,"one"),make_pair(2,"two"),make_pair(3,"three"),make_pair(4,"four"),make_pair(5,"five"),make_pair(6,"six") };map<int, string> m1(arr1.begin(), arr1.end());cout << "迭代器遍历m1:" << endl;map<int, string>::iterator it1 = m1.begin();while (it1 != m1.end()){cout << it1->first << " " << it1->second << endl;it1++;}cout << endl;cout << "插入元素后" << endl;m1.insert({ 7,"seven" });for(auto& e : m1)cout << e.first << " " << e.second << endl;cout << "根据键值 删除元素后" << endl;m1.erase(1);m1.erase(7);for (auto& e : m1)cout << e.first << " " << e.second << endl;cout << endl;cout << "构造新的m2" << endl;vector<pair<int, string>> arr2 = { make_pair(11,"eleven"),make_pair(12,"twelve"),make_pair(13,"thirteen"),make_pair(14,"fourteen"),make_pair(15,"fifteen"),make_pair(16,"sixteen") };map<int, string> m2(arr2.begin(), arr2.end());for (auto& e : m2)cout << e.first << " " << e.second << endl;cout << "m1和m2交换后:" << endl;m1.swap(m2);cout << "m1:" << endl;for (auto& e : m1)cout << e.first << " " << e.second << endl;cout << "m2:" << endl;for (auto& e : m2)cout << e.first << " " << e.second << endl;cout << endl<< "根据键值查找元素:" << endl;cout << "m1.find(11): ";cout << (m1.find(11) != m1.end()) << endl << endl;cout << "m1的size:" << m1.size() << endl;cout << "m2的size:" << m2.size() << endl;cout << endl;map<int, int> m3;cout << "m1的empty:" << m1.empty() << endl;cout << "m3的empty:" << m3.empty() << endl;return 0;
}

3、map的operator[ ]

operator[]的原理是:

用<key, T()>构造一个键值对,然后调用insert()函数将该键值对插入到map中

如果key已经存在,插入失败,insert函数返回该key所在位置的迭代器

如果key不存在,插入成功,insert函数返回新插入元素所在位置的迭代器

operator[]函数最后将insert返回值键值对中的value(实值)返回。

 

也就是说:

operator[ ]返回的是当前键值对应的实值,如果当前键值不存在,则会插入新的键值对,然后在返回新的键值对的实值。

int main()
{vector<pair<int, string>> arr1 = { make_pair(1,"one"),make_pair(2,"two"),make_pair(3,"three"),make_pair(4,"four"),make_pair(5,"five"),make_pair(6,"six") };map<int, string> m1(arr1.begin(), arr1.end());cout << m1[1] << endl;cout << m1[3] << endl;cout << m1[7] << endl;m1[7] = "seven";m1[8];m1[9] = "nine";for (auto& e : m1)cout << e.first << " " << e.second << endl;return 0;
}

应用(计数)

int main()
{vector<string> v1 = { "苹果","香蕉","西瓜","西瓜" ,"西瓜" ,"西瓜","香蕉","香蕉","苹果" };map<string, int> m1;for (auto& e : v1){map<string, int>::iterator ret = m1.find(e);if (ret == m1.end())m1.insert(make_pair(e, 1));elseret->second++;}for (auto& e : m1)cout << e.first << ":" << e.second << endl;return 0;
}

那么operator[]就可以优化上述的代码:

int main()
{vector<string> v1 = { "苹果","香蕉","西瓜","西瓜" ,"西瓜" ,"西瓜","香蕉","香蕉","苹果" };map<string, int> m1;for (auto& e : v1)m1[e]++;for (auto& e : m1)cout << e.first << ":" << e.second << endl;return 0;
}

3、multimap:

multimap和map的关系,和multiset和set的关系差不多。都是将容器中不能重复的值 变为 可以存在重复的值。

int main()
{vector<pair<int, string>> arr1 = { make_pair(1,"one"),make_pair(2,"two"),make_pair(3,"three"),make_pair(4,"four"),make_pair(5,"five"),make_pair(6,"six") };multimap<int, string> m1(arr1.begin(), arr1.end());map<int, string> m2(arr1.begin(), arr1.end());m1.insert(make_pair(2, "two"));m1.insert(make_pair(2, "two"));m1.insert(make_pair(2, "two"));m1.insert(make_pair(7, "seven"));cout << "multimap<int, string> m1(arr1.begin(), arr1.end());" << endl;for (auto& e : m1)cout << e.first << " " << e.second << endl;cout << endl;m2.insert(make_pair(2, "two"));m2.insert(make_pair(2, "two"));m2.insert(make_pair(2, "two"));m2.insert(make_pair(7, "seven"));cout << "map<int, string> m2(arr1.begin(), arr1.end());" << endl;for (auto& e : m2)cout << e.first << " " << e.second << endl;return 0;
}

注意:

因为multimap可以存在数据重复,那么就不能够提供[]运算符,因为不知道是修改的哪一个

map可以随便修改value的值

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AOP-代理实现

三种代理实现 1 JDK动态代理实现-基于接口代理 2 CGLIB动态代理实现-基于类代理 3 AspectJ 适配实现 为什么Proxy.newProxyInstance 会生成新的字节码&#xff1f; 创建代理类&#xff1a; Proxy.newProxyInstance 首先会检查缓存中是否有已存在的代理类字节码。 如果没有&…

计算机毕业设计 C语言学习辅导网站的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

14.安卓逆向-frida基础-编写hook脚本2

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a;图灵Python学院 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要盲目相信。 工…

16. C++ TinyWebServer项目总结(16. 服务器调制、调试和测试)

主要包括&#xff1a; 使用 tcpdump 抓包&#xff1b;使用 gdb 调试器&#xff1b;使用压力测试工具&#xff0c;模拟现实世界中的高并发请求&#xff0c;测试服务器在高压状态下的稳定性。 最大文件描述符数 Linux 对应用进程能打开的最大文件描述符数量有两个层次的限制&a…

node的版本管理工具volta

安装方式 # mac curl https://get.volta.sh | bash # Windows Installation winget install Volta.Volta切换版本 volta install node指定版本根据项目固定node和包管理器版本和 该命令会在package.json生成volta的配置&#xff0c;volta会自动读取项目的该配置来决定node的…

【STM32】TCP/IP通信协议--LWIP内存管理

五、LWIP内存管理 1.什么是内存管理&#xff1f; &#xff08;1&#xff09;内存管理&#xff0c;是指软件运行时对计算机内存资源的分配的使用的技术&#xff0c;其主要目的是如何高效、快速的分配&#xff0c;并且在适当的时候释放和回收内存资源&#xff08;就比如C语言当…

安全的价值:构建现代企业的基础

物理安全对于组织来说并不是事后才考虑的问题&#xff1a;它是关键的基础设施。零售商、医疗保健提供商、市政当局、学校和所有其他类型的组织都依赖安全系统来保障其人员和场所的安全。 随着安全技术能力的不断发展&#xff0c;许多组织正在以更广泛的视角看待他们的投资&am…

SQL学习1

24.9.28学习目录 一.数据库1.SQL语句基础2.匹配条件 一.数据库 对于嵌入式的数据库&#xff0c;其使用的是SQLite这种小型数据库&#xff1b; 在ubuntu中的下载方法 //字符界面 sudo apt-get install sqlite3//图形界面 sudo apt-get install sqlitemanSQLite特点&#xff1a…

ACL 2023--MetaAdapt: 通过元学习实现领域自适应的少量样本虚假信息检测

https://github.com/Yueeeeeeee/MetaAdapt 随着社交媒体上出现的新话题&#xff08;例如COVID-19&#xff09;成为虚假信息传播的来源&#xff0c;克服原始训练领域&#xff08;即源领域&#xff09;与这些目标领域之间的分布变化&#xff0c;仍然是虚假信息检测中的一项复杂任…

5分钟精通Excel在go中的使用

一些简单操作可以在官方文档中找到&#xff0c;应该足够无经验的朋友们入门 介绍 - 《Excelize v2.2 中文文档》 - 书栈网 BookStack 这里贴一个中文版的链接&#xff08;以excelize库为例&#xff0c;相对其他库来说&#xff0c;体验很不错&#xff09;&#xff0c;不过要注…

PWA(Progressive web APPs,渐进式 Web 应用): manifest.json、 Service Worker

文章目录 引言I 什么是 PWA功能特性技术上分为三个部分安装应用II Web 应用清单将Web 应用清单文件链接到站点manifest.json字段说明III Service Worker( 缓存管理)IV 结合构建工具让项目支持 PWA应用使用插件vite-plugin-pwaworkbox-webpack-plugin插件扩展知识将 PWA 作为脱机…

紫光 FPGA固化RAM位置的操作流程

1. 前提条件&#xff1a;需要已经编译出一个功能完整的没有时序违例的版本出来&#xff1b; 2. 将RAM导出至txt文件&#xff1a; 这个过程需要几分钟&#xff0c;耐心等待一下。 等待提示成功就可以进行下一步操作了。 3. 将【2】中的txt文件中的内容全选复制粘贴到pcf文件的…

物体实例分割,机器人拾取

物体实例分割是计算机视觉领域的一个关键任务&#xff0c;它旨在从图像中分割出每个独立物体&#xff0c;并且为每个物体实例提供一个独特的标识。这一任务不仅识别出图像中的物体&#xff0c;还能区分出多个同类物体的不同实例&#xff0c;例如在一张桌子上摆放的多个相同的杯…

AI直播巅峰!2024年AI无人直播app排行榜领先者揭晓!

AI直播巅峰&#xff01;2024年AI无人直播app排行榜领先者揭晓&#xff01; 在科技日新月异的今天&#xff0c;AI技术正以惊人的速度渗透到我们生活的每一个角落&#xff0c;其中&#xff0c;AI无人直播app的兴起无疑成为了直播行业的一股革新力量。随着技术的不断成熟和市场的…

瓶子类型检测系统源码分享

瓶子类型检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vis…

论文阅读:A Generalization of Transformer Networks to Graphs

论文阅读&#xff1a;A Generalization of Transformer Networks to Graphs 论文地址1 摘要2 贡献Graph TransformerOn Graph Sparsity&#xff08;图稀疏&#xff09;On Positional Encodings&#xff08;位置编码&#xff09;3 Graph Transformer Architecture&#xff08;架…

关于Fake Location定位,运动世界校园问题

不好意思&#xff0c;之前那个文章其实是很早之前的&#xff0c;不知道为什么审核了很久一直没有通过&#xff0c;然后前几周莫名其妙点了一下重新发布&#xff0c;竟然发布成功了&#xff0c;这个方法已经失效了&#xff0c;要可以稳定&#xff0c;我建议是买一台root的手机&a…

给Ubuntu虚拟机设置静态IP地址(固定IP)

查看 为Ubuntu虚拟机配置静态IP地址&#xff08;固定IP&#xff09;的方法经过亲自测试&#xff0c;已被证实有效。 这里你记得网关就可以了&#xff0c;等下要用 查看配置前的网络信息 ifconfig 查看网关 route -n 配置 配置网络文件 cd /etc/netplan/ ls 查看自己的文件的名…

o1规划能力首测!已超越语言模型范畴,preview终于赢mini一回

克小西 发自 凹非寺 量子位 | 公众号 QbitAI o1-preview终于赢过了mini一次&#xff01; 亚利桑那州立大学的最新研究表明&#xff0c;o1-preview在规划任务上&#xff0c;表现显著优于o1-mini。 相比于传统模型的优势更是碾压级别&#xff0c;在超难任务上的准确率比Llama3.…