【STL】 set 与 multiset:基础、操作与应用

news2024/9/21 20:49:06

在 C++ 标准库中,set 和 multiset 是两个非常常见的关联容器,主要用于存储和管理具有一定规则的数据集合。本文将详细讲解如何使用这两个容器,并结合实例代码,分析其操作和特性。

0.基础操作概览

0.1.构造:

set<T> st;                   
// 默认构造函数:

set(const set& st);          
//拷贝构造函数

0.2.赋值:

set& operator=(const set& st); 
//重载等号操作符

0.3.统计set容器大小以及交换set容器

size();                       
//返回容器中元素的数目

empty();                       
//判断容器是否为空

swap(st);                     
 //交换两个集合容器

0.4.set容器进行插入数据和删除数据

insert(elem);                  
//在容器中插入元素。      

clear();                       
// 清除所有元素
erase(pos);                         
//删除pos迭代器所指的元素,返回下一个元素的迭代器。

erase(beg, end);                   
//删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。

erase(elem);                       
//删除容器中值为elem的元素。

0.5.set查找和统计:对set容器进行查找数据以及统计数据

find(key);          
//查找key是否存在,若存在,返回该键的元素的迭代器;
//若不存在,返回set.end();

count(key);                        
//对于set而言,统计key的元素个数
//只有两种结果:如果容器中不存在key,返回0; 否则,返回1

0.6.排序

set<int> st1;               
// 储存int的集合(从小到大)

set<int, greater<int>> st2; 
// 储存int的集合(从大到小)

1. set 与 multiset 的基本概念

  • set:它是一种自动去重且按顺序排列的集合。每次插入元素时,set 会自动判断该元素是否已存在,若存在则不会插入。
  • multiset:允许重复元素的集合,因此可以存储多个相同的元素。
  • 关联容器:关联容器中的元素在插入时自动排序,因此不同于顺序容器如 vector 或 list,不需要手动排序。

2. set 容器的构造与赋值

在 C++ 中,set 提供了多种构造方式:

  • 默认构造set <int> st; 创建一个空的整数集合。
  • 拷贝构造set<int> st2(st); 从已有的集合 st 创建一个副本。
  • 赋值操作set& operator=(const set& st);可以通过重载的 = 操作符将一个集合的内容赋值给另一个集合。

构造与赋值实例:

void test0()
{
    // 1.默认构造
    set<int>s;
    s.insert(1);  // 插入元素1
    s.insert(-4); // 插入元素-4
    s.insert(2);  // 插入元素2
    s.insert(5);  // 插入元素5
    s.insert(8);  // 插入元素8
    s.insert(1);  // 插入重复元素1,自动忽略

    print(s);  // 打印集合中的元素,输出为:-4 1 2 5 8

    cout << endl;
    // 2.拷贝构造
    set<int>s2(s);  // 拷贝构造函数
    print(s2);  // 输出:-4 1 2 5 8

    cout << endl;
    // 3.赋值操作
    set<int>s3 = s2;  // 赋值操作符
    print(s3);  // 输出:-4 1 2 5 8
}

在这里插入图片描述

在此代码中,set 的插入操作可以看出,重复元素(如插入的 1)不会出现在集合中,这是 set 自动去重的特性。

3.遍历 set 容器

在 set 中,不能使用下标访问元素,因此遍历集合有两种常见方式:

  • 使用迭代器遍历:通过迭代器访问集合元素,适合所有关联容器。
  • 基于范围的 for 循环:简化迭代器操作,代码更加简洁。

迭代器遍历:

void print(set<int>& s)
 {
    for (set<int>::iterator it = s.begin(); it != s.end(); it++) 
    {
        cout << *it << " ";  // 输出每个元素
    }
    cout << endl;
    
    /*for (auto it = s.begin(); it != s.end(); it++)
	{
		cout << *it << " ";
	}*/
}

基于范围的 for 循环

void print(set<int>& s) 
{
    for (int elem : s) 
    {
        cout << elem << " ";  // 直接输出元素
    }
    cout << endl;
    
    /*for (auto it : s)
	{
		cout << it << " ";
	}*/
}

降序遍历:如果集合是降序排序的,例如使用
set<int, greater>,需要在定义迭代器时添加相应的比较器。

void print(set<int, greater<int>>& s) 
{
    for (set<int, greater<int>>::iterator it = s.begin(); it != s.end(); it++) 
    {
        cout << *it << " ";  // 输出降序排列的元素
    }
    cout << endl;
}

遍历操作

void test_traversal() 
{
    set<int> s;
    s.insert(5);
    s.insert(1);
    s.insert(3);
    s.insert(7);
    
    cout << "使用迭代器遍历 set:" << endl;
    print(s);  // 输出:1 3 5 7

    cout << "使用基于范围的 for 循环遍历 set:" << endl;
    print2(s);  // 输出:1 3 5 7
}

总结:遍历 set 的两种方式都非常直观,迭代器方式适合所有关联容器,而基于范围的 for 循环则能让代码更简洁。

5. set 容器的大小统计与交换

  • size();返回集合中的元素数量。
  • empty();检查集合是否为空,若为空返回 true,否则返回false。
  • swap();交换两个集合的内容。

统计大小与交换

void test1()
{
	//构造2个有数据的set容器
	set<int>s;
	s.insert(0);
	s.insert(1);
	s.insert(0);
	s.insert(9);
	s.insert(6);
	s.insert(11);
	

	set<int>s1;
	s1.insert(0);
	s1.insert(0);
	s1.insert(0);
	s1.insert(99);
	s1.insert(4);
	s1.insert(-1);


	cout << endl;
	
	//判断是否为空
	if (!s.empty()) 
	{
		cout << "s容器的长度为:" << s.size() << endl;
	}
	

	cout << endl;
	
	//交换
	cout<< "交换前:" << endl;
	cout << "s: " ;
	print(s);
	cout << endl << "s1:";
	print(s1);

	s.swap(s1);

	cout << endl << endl;
	cout << "交换后:" << endl ;
	cout << "s: " ;
	print(s);
	cout << endl << "s1: ";
	print(s1);
}

在这里插入图片描述

通过 swap() 操作,s 和 s1 的内容得到了交换。这样可以有效简化代码,避免使用临时变量来保存集合的内容。

6. set 容器的插入与删除操作

  • insert(elem);向集合中插入元素。由于 set 自动排序,插入元素时不会指定位置。

删除元素有三种方式:

  • erase(pos); 删除pos迭代器所指的元素,返回下一个元素的迭代器。

  • erase(beg, end); 删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。

  • erase(elem); 删除容器中值为elem的元素。

void test2()
{
	set<int>s1;
	s1.insert(100);
	s1.insert(100);
	s1.insert(20);
	s1.insert(0);
	s1.insert(44);
	s1.insert(12);
	s1.insert(666);
	s1.insert(9);

	print(s1);

	cout << endl;
	//删除迭代器指定元素
	s1.erase(s1.begin());//这里删掉的是第一个元素0,因为set容器自动排序
	print(s1);

	cout << endl;
	//删除指定元素
	s1.erase(20);
	print(s1);

	cout << endl;
	//删除迭代器指定区间元素
	s1.erase(s1.begin(), s1.end());
	//等价于清空操作:
	//s1.clear();
	cout <<"当前s1的大小为:" << s1.size() << endl;
}

在这里插入图片描述

在此代码中,我们展示了三种不同的 erase 操作,可以方便地删除单个元素或一组元素。

7. set 容器的查找与统计

find(key);
查找key是否存在,若存在,返回该键的元素的迭代器;
若不存在,返回set.end();

count(key);
对于set而言,统计key的元素个数
只有两种结果:如果容器中不存在key,返回0; 否则,返回1

查找与统计:

void test3()
 {
    set<int>s1;
    s1.insert(1);
    s1.insert(100);
    s1.insert(23);
    s1.insert(0);
    s1.insert(404);
    s1.insert(12);
    s1.insert(999);
    s1.insert(9);

    // 查找元素 404
    auto it = s1.find(404);
    if (it != s1.end()) {
        cout << "找到了元素 404" << endl;  // 输出:找到了元素404
    } else {
        cout << "未找到元素 404" << endl;
    }

    // 使用 count 统计元素个数
    int n = s1.count(404);
    cout << "s1 中元素 404 的个数为:" << n << endl;  // 输出:1
}

通过 find() 和 count() 函数,可以轻松实现元素的查找与存在性检查。

8. set 容器的排序特性

默认情况下,set 按照升序排列。可以通过
set<int, greater<int>> 指定降序排列。

  • set<int> st1; 储存int的集合(从小到大)
  • set<int, greater<int>> st2; 储存int的集合(从大到小)
    排序:
void test4()
{
	//默认构造
	set<int>s1;
	//等价于set<int, less<int>>s1;
	s1.insert(10);
	s1.insert(40);
	s1.insert(20);
	s1.insert(0);
	s1.insert(-10);

	print(s1);

	//降序构造,用到比较器greater(类型)
	cout << endl;
	set<int, greater<int>>s2;
	s2.insert(10);
	s2.insert(40);
	s2.insert(20);
	s2.insert(0);
	s2.insert(-10);

	print2(s2);
}

通过比较器 greater 可以实现集合的降序排列。

9.相关注意事项

9.1. 自动排序与元素唯一性

自动排序:set 会自动将插入的元素按升序(或根据提供的自定义比较器排序)进行排序。因此,插入顺序与实际存储顺序可能不同。

注意:由于 set 是自动排序的容器,插入操作可能会引发排序操作,这使得插入操作的平均时间复杂度为 O(log n),适合需要快速查找和去重的场景。

元素唯一性:set 不允许重复元素。如果插入的元素已经存在于集合中,set 会自动忽略该元素。即使多次插入相同的值,集合中只会保留一个副本。

set<int> s;
s.insert(1);
s.insert(1);  // 插入重复元素,set 会自动忽略
print(s);     // 输出:1

9.2. 不能使用下标访问

与 vector 不同,set 作为关联容器不能通过下标访问元素,也不能像顺序容器那样直接修改元素。只能通过迭代器或基于范围的 for 循环来遍历集合。

set<int> s = {10, 20, 30};
// s[0] = 5;  // 错误!set 不能使用下标
for (int elem : s) 
{
    cout << elem << " ";  // 正确的遍历方式
}

9.3. 修改元素的限制

在 set 中,由于其自动排序的特性,不能直接通过迭代器修改元素的值如果需要修改元素值,必须先删除该元素,然后插入新的值。

set<int> s = {10, 20, 30};
auto it = s.find(20);
if (it != s.end()) 
{
    s.erase(it);     // 先删除 20
    s.insert(25);    // 再插入新值 25
}
print(s);  // 输出:10 25 30

9.4. 迭代器的有效性

当删除或插入元素时,set 的迭代器可能会失效。特别是在删除操作中,如果需要使用迭代器操作,建议在删除之后重新获取下一个有效的迭代器。

set<int> s = {1, 2, 3, 4, 5};
auto it = s.begin();
while (it != s.end()) 
{
    if (*it == 3) 
    {
        it = s.erase(it);  // erase 返回下一个有效迭代器
    } 
    else 
    {
        it++;
    }
}
print(s);  // 输出:1 2 4 5

9.6. 自定义排序规则

如果需要按自定义顺序排序 set 中的元素,可以通过传入自定义的比较器来改变排序方式。例如,可以使用 greater 来实现降序排列,或者提供自定义的比较函数。

set<int, greater<int>> s = {10, 20, 30};
print2(s);  // 输出:30 20 10

9.8. 避免重复调用 find 和 count

如果你想要同时查找元素和统计某个元素的出现次数,不必重复调用 find() 和 count(),因为 count() 可以直接返回是否存在目标元素(对于 set,返回值只会是 0 或 1)。count() 本质上相当于 find() 的简化版。

set<int> s = {10, 20, 30};
if (s.count(20)) 
{
    cout << "元素 20 存在" << endl;
}

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

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

相关文章

CefSharp_Vue交互(Element UI)_WinFormWeb应用(3)---通过页面锁屏和关机(含示例代码)

一、预览 实现功能:通过vue标题栏按钮锁屏和关机 1.1 预览 1.2 代码 锁屏代码csharp LockWorkStation() 关机代码chsharp 注意vue代码参数和此参数一致(0/1/2) 方法ExitWindowsEx()

春秋云境靶场之CVE-2022-29464

一.靶场环境 1.下载靶场 根据题目提示&#xff0c;存在文件上传漏洞 2.启动靶场 打开之后&#xff0c;页面显示 然后就跳转到一个登录页面 二.登录页面 1.尝试登录 我们尝试弱口令登录admin,admin&#xff0c;跳转到连接超时页面 当我们再次点击这个链接后&#xff0c;就会…

Leetcode面试经典150题-20.有效的括号

给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。每个右括号都有一个对应的相同类型的左括…

【网络原理】❤️Tcp 常用机制❤️ —— 延时应答,捎带应答, 面向字节流, 异常情况处理。保姆式详解 , 建议收藏 !!!

本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人…

【程序员写的诗】《享平安》日期:2021-11-03 作者:橙

享平安 《享平安》 关关难过&#xff0c;关关过。 世事无常&#xff0c;平常心。 顾好自己&#xff0c;和家人。 平平安安&#xff0c;享人生。 创作背景 背景&#xff1a;新冠疫情 涉及32个省&#xff0c;河南郑州未能辛免 ------写于2021-11-03 程鹏 AI豆包点评和解释 这…

前端vue3打印,多页打印,不使用插件(工作中让我写一个打印功能)

说下总体思路&#xff0c;创建一个组件&#xff0c;里面放多个span字段&#xff0c;然后根据父组件传入的参数&#xff0c;生成子组件&#xff0c;最好我们打印子组件的信息即可。通过我多次ai&#xff0c;探索最后成功了。 子组件代码 media print 这个我要讲一下&#xff…

泛读笔记:从Word2Vec到BERT

自然语言处理(NLP)模型的发展历史 1.统计方法时期&#xff1a;使用贝叶斯方法、隐马尔可夫模型、概率模型等传统统计方法 2.机器学习时期&#xff1a;支持向量机(SVM)、决策树模型、随机森林、朴素贝叶斯等传统机器学习方法 3.深度学习革命&#xff1a;各种新的深度学习模型&am…

Day28_0.1基础学习MATLAB学习小技巧总结(28)——参数估计函数

利用空闲时间把碎片化的MATLAB知识重新系统的学习一遍&#xff0c;为了在这个过程中加深印象&#xff0c;也为了能够有所足迹&#xff0c;我会把自己的学习总结发在专栏中&#xff0c;以便学习交流。 参考书目&#xff1a; 1、《MATLAB基础教程 (第三版) (薛山)》 2、《MATL…

流程图怎么画?3个好用的在线流程图软件推荐,绘图没烦恼!

目录 什么是流程图&#xff1f; 为什么需要使用流程图&#xff1f; 流程图中各种图形的含义 如何制作流程图&#xff1f; 小结&#xff1a;流程图如何制作&#xff1f; 流程图是表达工作流程或者系统操作过程的有效工具&#xff0c;被广泛应用于各个行业和领域。…

USDT自动化交易【Pinoex】【自动化分析】【ChatGPT量化脚本】

Pinoex 是一个相对较新的加密货币交易平台&#xff0c;虽然具体的自动交易算法细节对外部用户可能并不公开&#xff0c;但我们可以讨论一般情况下加密货币自动交易算法的常见策略和方法。以下是一些可能会被类似平台或个人交易者使用的自动交易算法和策略。 1. 市场制造商&…

数据结构——原来二叉树可以这么学?(4.链式二叉树)

前言&#xff1a; 在前两篇小编讲述了二叉树的顺序结构存储方式&#xff0c;从而讲到了一个特殊的二叉树——堆&#xff0c;通过对于堆的实现来帮助我们对于顺序结构存储方式的实现&#xff0c;下面小编将要讲述二叉树的另一种存储方式——链式结构&#xff0c;链式二叉树来喽&…

专业学习|GERT网络概览(学习资源、原理介绍、变体介绍)

一、GERT 网络概览 GERT(Graphical Evaluation Review Technique&#xff0c;图示评审技术)是一种结合流线图理论(Flow Graphical Theory)、矩母函数(Moment Generating Function)、计划评审技术(Program Evaluation Review Technique)解决随机网络问题的方法&#xff0c;描述各…

2024年CAD图纸加密软件|加密图纸软件推荐:10款高效CAD加密软件

在当今数字化时代&#xff0c;CAD图纸已成为工程设计、建筑规划、机械制造等领域不可或缺的重要文件。然而&#xff0c;随着数据泄露和信息安全问题的日益严重&#xff0c;保护CAD图纸的安全性变得尤为重要。为了确保设计数据的安全&#xff0c;使用高效的CAD图纸加密软件成为了…

CMAT:提升小型语言模型的多智能体协作调优框架

人工智能咨询培训老师叶梓 转载标明出处 大模型&#xff08;LLMs&#xff09;已经成为自然语言处理&#xff08;NLP&#xff09;的基石。然而&#xff0c;这些模型的有效运行仍然在很大程度上依赖于人为输入来准确引导对话流程。为了解决这一问题&#xff0c;来自华东交通大学…

comfyui中报错 Cmd(‘git‘) failed due to: exit code(128) 如何解决

&#x1f388;背景 comfyui今天在安装插件的过程中&#xff0c;发现有个插件第一次安装失败后&#xff0c;再次安装就开始报错了&#xff0c;提示&#xff1a; ComfyUI-Inpaint-CropAndStitch install failed: Bad Request 截图如下&#xff1a; 看下后台的报错&#xff1a; …

Html css水平居中+垂直居中+水平垂直居中的方法总结

1. Html css水平居中垂直居中水平垂直居中的方法总结 1.1. Html 元素 1.1.1.元素宽高特点 &#xff08;1&#xff09;块级元素&#xff08;如div&#xff09;&#xff1a;独占一行&#xff0c;不和其他元素在一起&#xff0c;可以设置宽和高&#xff1b;当没设置宽和高时&am…

宝塔面板FTP连接时“服务器发回了不可路由的地址。使用服务器地址代替。”

参考 https://blog.csdn.net/neizhiwang/article/details/106628899 错误描述 我得服务器是腾讯&#xff0c;然后使用宝塔建了个HTML网站&#xff0c;寻思用ftp上传&#xff0c;结果报错&#xff1a; 状态: 连接建立&#xff0c;等待欢迎消息... 状态: 初始化 TLS 中... 状…

go多线程

1、简单使用&#xff08;这个执行完成&#xff0c;如果进程执行比较久&#xff0c;这里不会等待它们结束&#xff09; package mainimport "time"func main() {go func() {println("Hello, World!")}()time.Sleep(1 * time.Second) }2、wg.Add(数量)使用&…

nginx服务介绍

nginx 安装使用配置静态web服务器 Nginx是一个高性能的Web服务器和反向代理服务器&#xff0c;它最初是为了处理大量并发连接而设计的。Nginx还可以用作负载均衡器、邮件代理服务器和HTTP缓存。它以其轻量级、稳定性和高吞吐量而闻名&#xff0c;广泛用于大型网站和应用中 Ngin…

2024ICPC网络赛1: C. Permutation Counting 4

题意&#xff1a; 给定 n n n个区间 [ L i , R i ] [L_i,R_i] [Li​,Ri​]设集合 A { { p i } ∣ p i 为排列&#xff0c; L i < p i < R i } A\{ \{ p_i\} | p_i为排列&#xff0c;Li<p_i<R_i\} A{{pi​}∣pi​为排列&#xff0c;Li<pi​<Ri​}&#xff…