set的使用

文章目录

  • 一、关联式容器
  • 二、set
      • 1、set的介绍
      • 2、set的使用
        • 2.1、元素的插入(insert接口)
        • 2.2、pair的简单讲解
        • 2.3、元素的查找(find接口)
        • 2.4、判断元素是否在set中(count接口)
        • 2.5、元素的删除(erase接口)
        • 2.6、区间查找:lower_bound和upper_bound
        • 2.7、查找特定值的范围:equal_range
    • 2、multiset(有重复元素的set)
      • 2.1、multiset的介绍
      • 2.2、multiset的使用

一、关联式容器

在之前的学习中,我们都接触了不少容器,如vector、list、deque等容器,这些容器统称为 序列式容器。而stack和queue是靠deque适配出来的,之前我们也模拟实现了,所以stack和queue是 容器适配器。今天我们还要学习一种容器:关联式容器,其中set、multiset、map和multimap都叫做 关联式容器,下面重点学习 set和multiset

关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是 <key, value>结构的键值对,在数据检索时比序列式容器效率更高。

所以容器分为三种类型:序列式容器、关联式容器、容器适配器

二、set

1、set的介绍

image-20241221140639647

这是set的模板参数列表:

T:set中存放元素的类型,实际上在底层是<value, value>的键值对。
Compare:默认是使用小于来比较的。(可以使用仿函数修改)
Alloc:set中元素空间管理的方式,使用allocator空间配置器管理。

它的底层是**平衡二叉搜索树(红黑树)**实现的,后续我们会模拟实现。

2、set的使用

2.1、元素的插入(insert接口)

image-20241221141538705

insert插入元素一共有三种方式

①直接插入一个元素

②在迭代器的位置插入一个元素

③使用范围插入函数

#include <iostream>
#include <set>
using namespace std;
int main()
{set<int> myset;myset.insert(3);myset.insert(5);myset.insert(3);myset.insert(1);myset.insert(1);myset.insert(7);set<int>::iterator it = myset.begin();while (it != myset.end()){// *it = 1;     set中是不允许修改的 cout << *it << " ";it++;}return 0;
}

结果:image-20241221142512228

可以看到这里我们插入的元素是[3, 5, 3, 1, 1, 7],但是打印出来的值是[1, 3, 5, 7]。

image-20241221143206587

很明显,set是**去重+排序**的,保证了set中的元素是唯一的,这就是set的特性。

而且set同样是支持迭代器,你可以使用迭代器或者范围for来访问容器中的元素。

image-20241221143548181

对于set中的元素为什么不能修改?

  • set的元素是唯一的,如果你进行修改,保不齐你修改的元素正好set中就有的。
  • 修改了元素,不能保证set还是有序的,那么就必须重新调整树,这样效率是非常低的。
2.2、pair的简单讲解

官方文档说的很清楚,insert是有个返回值的:pair<iterator, bool>,这个是什么呢?这其实就是键值对,常常说的<K, V>就是键值对关系。

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

比如对于上述的insert的返回值,我们就可以打印出来看看。

int main()
{set<int> myset;myset.insert(3);myset.insert(5);pair<set<int>::iterator, bool> ret = myset.insert(3);cout << *ret.first << ":" << ret.second << endl;return 0;
}

结果:image-20241221144954913

当set中已经有了3之后,你再插入3进去就会失败。因为bool返回的0,也就是false。

2.3、元素的查找(find接口)

image-20241221145205146

返回值很简单,如果找到该元素了,就返回该值的迭代器;否则就返回end。

int main()
{set<int> myset;myset.insert(3);myset.insert(5);myset.insert(3);if(myset.find(3) != myset.end()){cout << *myset.find(3) << "找到了" << endl;}return 0;
}

结果: image-20241221151357993

set的底层是平衡二叉搜索树,查找一个元素的效率就是树的高度,为logN

2.4、判断元素是否在set中(count接口)

set中判断一个元素在不在set中,除了使用find以外,还有一个count接口可以判断。

image-20241221151811245

它有一个返回值,如果存在就返回1,否则返回0。

int main()
{set<int> myset;myset.insert(3);if(myset.count(3))cout << *myset.find(3) << "找到了" << endl;return 0;
}

结果:image-20241221152013126

find接口:if(myset.find(3) != myset.end()),count接口:if(myset.count(3))。

可以看出count接口的代码要少一点,所以,以后想要判断一个元素是否在set中,使用count接口更爽一点。

2.5、元素的删除(erase接口)

image-20241221152720899

set中erase一共有三种方式:迭代器位置删除,直接删除值,迭代器区间删除。

1.迭代器位置删除(这个很少用):

int main()
{set<int> myset;myset.insert(3);myset.insert(5);myset.insert(3);myset.insert(1);myset.insert(1);myset.insert(7);cout << "删除前set中的元素:";for (auto e : myset)cout << e << " ";cout << endl;auto it = myset.find(1);if (it != myset.end()){myset.erase(it);}cout << "删除后set中的元素:";for (auto e : myset)cout << e << " ";cout << endl;

结果:image-20241221153658345

2.直接删除值

int main()
{set<int> myset;myset.insert(3);myset.insert(5);myset.insert(3);myset.insert(1);myset.insert(1);myset.insert(7);cout << "删除前set中的元素:";for (auto e : myset)cout << e << " ";cout << endl;myset.erase(1);                  // 存在就直接删除myset.erase(10);                 // 删除不存在的值,也不会报错cout << "删除后set中的元素:";for (auto e : myset)cout << e << " ";cout << endl;return 0;
}

结果:image-20241221154154280

思考:对于迭代器位置删除和直接删除值,它们两个有什么区别呢?

答:迭代器删除需要使用 find + erase(pos) 来删除该元素,如果迭代器位置不存在,就会报错;而对于直接删除值来说,存在即删除,不存在,啥事不发生。

所以建议使用erase的时候,使用直接删除值的方式。

2.6、区间查找:lower_bound和upper_bound

image-20241221155103066

lower_bound和upper_bound是干什么的呢?看它们的参数和返回值,找寻一个值,然后返回一个迭代器位置。

  • lower_bound是找寻序列中第一个大于等于目标值的元素。

  • upper_bound是找寻序列中第一个大于目标值的元素。

#include <iostream>
#include <set>int main()
{std::set<int> myset;std::set<int>::iterator itlow, itup;for (int i = 1; i < 10; i++) myset.insert(i * 10); // 10 20 30 40 50 60 70 80 90itlow = myset.lower_bound(25);      // 返回30的位置         itup = myset.upper_bound(55);       // 返回60的位置              myset.erase(itlow, itup);           // 删除的区间是左闭右开的[itlow, itup)                   std::cout << "myset contains:";for (std::set<int>::iterator it = myset.begin(); it != myset.end(); ++it)std::cout << ' ' << *it;std::cout << '\n';return 0;
}

结果:image-20241221160142245

可以看到使用lower_bound和upper_bound可以找到元素的位置,然后通过erase就可以快速的删除区间的元素。

2.7、查找特定值的范围:equal_range

set中还有一个接口:equal_range,可以快速的找到目标值的lower_bound和upper_bound。

int main()
{std::set<int> myset;for (int i = 1; i <= 5; i++) myset.insert(i * 10);   // myset: 10 20 30 40 50std::pair<std::set<int>::const_iterator, std::set<int>::const_iterator> ret;ret = myset.equal_range(25);std::cout << "the lower bound points to: " << *ret.first << '\n';std::cout << "the upper bound points to: " << *ret.second << '\n';return 0;
}

结果:image-20241221160958708

总结:

1、set是一个关联式容器,插入元素只需要value即可,不需要键值对。

2、set可以去重 + 排序。

3、set的底层是红黑树。

4、查找某个元素,时间复杂度是logN。

5、set中的元素不能修改。

2、multiset(有重复元素的set)

2.1、multiset的介绍

set是不能存放重复元素,但是multiset可以存放重复的元素。

所以multiset是 排序 + 不去重

就是因为有无重复元素的影响,set和multiset的insert和erase有一点小小的区别。

set中的insert是会插入失败的(因为不允许有重复值),但是multiset的insert是不会插入失败的。

它的insert的返回值都不是pair类型的。

image-20241221164113624

对于erase来说,会删除所有和目标值相等的元素。

2.2、multiset的使用

int main()
{vector<int> vc = { 40,20,40,50,10 };multiset<int> myMulset(vc.begin(), vc.end());for (auto e : myMulset){cout << e << " ";}return 0;
}

结果:image-20241221165034375

为了得到是升序的序列,所以它的底层是通过中序访问。有很多相同的值,multiset中使用find接口的时候,找到的第一个元素应该是中序的第一个元素

OK,set和multiset的大致内容就是这些,下一个节我们将讲解map和multimap。

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

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

相关文章

[Xshell] Xshell的下载安装使用、连接linux、 上传文件到linux系统-详解(附下载链接)

前言 xshell 链接&#xff1a;https://pan.quark.cn/s/57062561e81a 提取码&#xff1a;TK4K 链接失效&#xff08;可能被官方和谐&#xff09;可评论或私信我重发 安装 下载后解压得到文件 安装路径不要有中文 打开文件 注意&#xff01;360等软件会拦截创建注册表的行为&a…

基于蜂鸟视图的智慧可视化巡检管理系统研究

摘要 本文围绕蜂鸟视图研发的智慧可视化巡检管理系统展开研究&#xff0c;系统依托室内地图和室内定位技术&#xff0c;覆盖“规划、巡场、检查、上报”的完整业务流程。核心功能包括基于蓝牙定位的巡检点位置验证、可视化巡场地图的在线规划与导航、以及巡检路线轨迹的回放分析…

GUI07-学工具栏,懂MVC

MVC模式&#xff0c;是天底下编写GUI程序最为经典、实效的一种软件架构模式。当一个人学完菜单栏、开始学习工具栏时&#xff0c;就是他的一生中&#xff0c;最适合开始认识 MVC 模式的好时机之一。这节将安排您学习&#xff1a; Model-View-Controller 模式如何创建工具栏以及…

Chrome 关闭自动添加https

Open Chrome and go to “chrome://net-internals/#hsts”

重拾设计模式--外观模式

文章目录 外观模式&#xff08;Facade Pattern&#xff09;概述定义 外观模式UML图作用 外观模式的结构C 代码示例1C代码示例2总结 外观模式&#xff08;Facade Pattern&#xff09;概述 定义 外观模式是一种结构型设计模式&#xff0c;它为子系统中的一组接口提供了一个统一…

gitlab代码推送

点击这个√ 修改的文件全部选上 填好提交的名称 点击commit 选取提交的 gitlab 库 点击Push

echarts画风向杆

1.安装echarts 2.引入echarts 4.获取数据&#xff0c;转换数据格式 windProfile.title.text ${moment(time.searchTime[0], ‘YYYY-MM-DD HH:mm:ss’).format( ‘YYYY-MM-DD HH:mm’ )}-${moment(time.searchTime[1], ‘YYYY-MM-DD HH:mm:ss’).format(‘YYYY-MM-DD HH:mm’)…

Java字符串的|分隔符转List实现方案

字符串处理 问题背景代码实现代码优化原因分析实现方案 注意事项异常处理Maven未识别异常 问题背景 在项目组对账流程中&#xff0c;接收对方系统的对账文件&#xff0c;数据以|为分隔符&#xff0c;读取文件内容&#xff0c;分条入库。 代码实现 Java中将字符串转给list&am…

「Mac畅玩鸿蒙与硬件47」UI互动应用篇24 - 虚拟音乐控制台

本篇将带你实现一个虚拟音乐控制台。用户可以通过界面控制音乐的播放、暂停、切换歌曲&#xff0c;并查看当前播放的歌曲信息。页面还支持调整音量和动态显示播放进度&#xff0c;是音乐播放器界面开发的基础功能示例。 关键词 UI互动应用音乐控制播放控制动态展示状态管理按钮…

iOS从Matter的设备认证证书中获取VID和PID

设备认证证书也叫 DAC, 相当于每个已经认证的设备的标识。包含了 VID 和 PID. 根据 Matter 对于设备证书的规定&#xff0c;DAC证书subject应该包含VID 和 PID. 可通过解析 X509 证书读取subject 来获得信息。 1 通过 SPM 添加X509 git地址&#xff1a;https://github.com/ap…

计算机毕业设计PyFlink+Hadoop广告推荐系统 广告预测 广告数据分析可视化 广告爬虫 大数据毕业设计 Spark Hive 深度学习 机器学

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

(2024.12)Ubuntu20.04安装openMVS<成功>.colmap<成功>和openMVG<失败>记录

一、安装openMVS 官方文档&#xff1a;https://github.com/cdcseacave/openMVS/wiki/Building sudo apt-get -y install git mercurial cmake libpng-dev libjpeg-dev libtiff-dev libglu1-mesa-dev eigen git clone https://gitlab.com/libeigen/eigen --branch 3.4 mkdi…

UE5 猎户座漂浮小岛 12 技能 瞬移 重力控制

1. 瞬移 1.1. 显示鼠标光标 “事件开始运行”添加显示鼠标逻辑 1.2. 释放技能蓝图 设置技能键 编写蓝图 1.3. 瞬移最大距离 2. 重力控制 2.1. 添加输入与动画 映射 重定向得到动画 新增状态FIRE_GracityControl 设置动画姿势 新增变量 切换动画 2.2. 技能蓝图&#xff08;…

叉车作业如何确认安全距离——UWB测距防撞系统的应用

叉车在工业环境中运行&#xff0c;常常需要在狭窄的空间内完成货物的搬运和堆垛&#xff0c;这对操作员的技术水平和安全意识提出了极高的要求。传统的叉车作业依赖操作员的经验和视觉判断来确认安全距离&#xff0c;然而这种方式往往存在误差&#xff0c;特别是在视线受阻或光…

深度学习每周学习总结J9(Inception V3 算法实战与解析 - 天气识别)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制 目录 0. 总结Inception V1 简介Inception V3 简介1. 设置GPU2. 导入数据及处理部分3. 划分数据集4. 模型构建部分5. 设置超参数&#xff1…

记录仪方案_记录仪安卓主板定制_音视频记录仪PCBA定制开发

记录仪主板采用了强大的联发科MTK8768处理器&#xff0c;拥有出色的性能表现。它搭载了四个主频为2.0GHz的Cortex-A53核心与四个主频为1.5GHz的Cortex-A53核心&#xff0c;确保了高效的处理速度。此外&#xff0c;主板配备了4GB的RAM(可选8GB)&#xff0c;并且内置64GB的ROM(可…

梳理你的思路(从OOP到架构设计)_简介设计模式

目录 1、 模式(Pattern) 是较大的结构​编辑 2、 结构形式愈大 通用性愈小​编辑 3、 从EIT造形 组合出设计模式 1、 模式(Pattern) 是较大的结构 组合与创新 達芬奇說&#xff1a;簡單是複雜的終極形式 (Simplicity is the ultimate form of sophistication) —Leonardo d…

JavaScriptEs6 - String类和Array类扩展内容

title: Javascript-ES6扩展写法 date: 2024-12-23 00:12:19 推荐在我的个人博客网站上访问本文章&#xff1a;shenying.website String 对象扩展 模版字符串 类似字符串的写法&#xff0c;用 来包裹字符串&#xff0c;优点是可以不用反斜杠就能在代码中多行编辑。对于模版字…

图书管理系统:提升图书馆服务质量的技术解决方案

可行性分析 在项目进行开发之前&#xff0c;必须要有可行性分析报告&#xff0c;分别从技术角度&#xff0c;经济角度&#xff0c;操作角度上面进行分析&#xff0c;经过可行性分析是实现科学开发的必要步骤。 3.1.1技术可行性 从技术的角度出发&#xff0c;目前采用开发的技术…

Unity中有什么情况下是需要用UniTask替代其他异步方式的吗?

在Unity开发中&#xff0c;是否需要使用UniTask替代其他异步方式&#xff08;如Coroutine或Task&#xff09;&#xff0c;取决于项目需求、代码风格和性能考量。UniTask是一个第三方库&#xff0c;主要用于优化和简化Unity环境下的异步编程&#xff0c;它提供了诸多优势&#x…