C++的set / multiset容器

一、介绍

        C++的set容器又被称为集合,所有元素在被插入后都会自动排序

二、数据结构

        set / multiset属于关联式容器,底层数据结构是用二叉树实现的。

        其余的容器比如vectordequelist等为序列式容器,因为他们底层使用线性序列结构,里面存储的是元素本身。而关联式容器所存储的是键值对<key,value>,这种方式相比于线性序列数据检索效率更高

        常见的关联式容器有:map、set、multimap、multiset,这四种容器的共同点是:

使用红黑树 / 平衡搜索树作为其底层结构,容器中的元素是一个有序的序列

三、set与multiset的区别

3.1 二者插入元素的区别

set不允许值相同的元素出现
multiset允许相同的元素出现

         在插入元素的时候,set的insert函数会返回bool类型的值,来表示插入是否成功。

注意  其实set的insert返回值是一个pair类型的变量,其中包含着bool,也有别的值)

而multiset的insert函数不会返回任何值。

3.2 pair “对组”类型

        pair类型,称为对组类型。它是由两种类型的数据组合的,数据的类型是模板(由自己确定)

对组的初始化有以下两种形式:

pair<type1, type2> p(value1, value2);

创造一个第一个变量类型为type1,第一个值为value1

第二个变量类型为type2,第二个值为value2的对组对象

pair<type1, type2> p = make_pair(value1, value2);同上

         对组的第一个变量为first,第二个变量为second。为了便于理解,请看以下例子:

//打印Pair变量的信息
void printPair(const pair<string, int>& p) {
	cout << p.first << "今年" << p.second << "岁." << endl;
}

int main() {
    pair<string, int> c("小C", 18);
    printPair(c);
    pair<string, int> daniel = make_pair("Daniel", 30);
    printPair(daniel);

    return 0;
}

         程序执行的结果如下所示:

四、set / multiset的使用

        为了便于观察,我们以下所有测试用例,均采用自己编写的printSet函数打印全部元素:

//打印set集合的所有元素
void printSet(const set<int> &s) {
	if (s.empty()) {
		cout << "This set is EMPTY now." << endl;

		return;
	}

	for (int elem : s) {
		cout << elem << ' ';
	}
	cout << endl;
}

4.1 set初始化

声明解释
set<T> s初始化一个空集合
set<T> s(const set<T> &s1)

拷贝构造函数,用s1的所有元素初始化一个集合

set<int> s;//初始化空集合
printSet(s);
set<int> s1(s);//拷贝构造函数
printSet(s);

         程序执行结果如下所示:

4.2 set赋值

        两个set对象之间的赋值采用重载后的=运算符

set<int> s0;//s0 = EMPTY
set<int> s1;//s1 = EMPTY

for (int i = 0; i < 10; i++) {
	s0.insert(i);
}//s0 = 0 1 2 3 4 5 6 7 8 9

cout << "Before s1 = s0, s1 : ";
printSet(s1);//s1 = EMPTY
s1 = s0;
cout << "Before s1 = s0, s1 : ";
printSet(s1);//s1 = 0 1 2 3 4 5 6 7 8 9

         程序执行结果如下所示:

4.3 set容量和大小

声明解释
empty()判断集合是否为空
size()

返回集合中元素的个数

set<int> s0;//s0 = EMPTY
set<int> s1;//s1 = EMPTY

for (int i = 0; i < 10; i++) {
	s0.insert(i);
}//s0 = 0 1 2 3 4 5 6 7 8 9

cout << "s0.empty() : " << boolalpha << s0.empty() << endl;//false
cout << "s0.size() : " << s0.size() << endl;//10

         程序的执行结果如下所示:

4.4 set交换元素

声明解释
swap(set<T> s1)将当前集合的全部元素和s1的全部元素交换
set<int> s0;
set<int> s1;

for (int i = 0; i < 10; i++) {
	s0.insert(i);
	s1.insert(i+10);
}

cout << "Before Swap : " << endl;
cout << "s0 : "; printSet(s0);
cout << "s1 : "; printSet(s1);

s0.swap(s1);//swap

cout << "After Swap : " << endl;
cout << "s0 : "; printSet(s0);
cout << "s1 : "; printSet(s1);

        程序如下所示:

4.5 set插入元素

声明解释
insert(T elem)将elem插入当前集合
set<int> s0;
cout << "Before Insert : " << endl;
cout << "s0 : "; printSet(s0);

//为s0 插入0~9
for (int i = 0; i < 10; i++) {
	s0.insert(i);
}

cout << "Atfer Insert : " << endl;
cout << "s0 : "; printSet(s0);

         程序运行的结果如下所示:

4.6 set删除元素

声明解释
erase(T elem)删除当前集合中值为elem的元素
erase(pos)

删除当前集合中pos迭代器指向的元素

erase(begin, end)删除当前集合中[begin, end)区间的元素
clear()删除当前集合中全部元素
set<int> s0;
for (int i = 0; i < 10; i++) {
	s0.insert(i);
}//s0 = 0 1 2 3 4 5 6 7 8 9 

//删除值为7的元素
s0.erase(7);
cout << "s0 : "; printSet(s0);//s0 = 0 1 2 3 4 5 6 8 9

//删除begin+1指向的元素
set<int>::iterator s_begin = s0.begin();
s_begin++;
s0.erase(s_begin);
cout << "s0 : "; printSet(s0);//s0 = 0 2 3 4 5 6 8 9

//删除[begin+2, end-2)
s_begin = s0.begin();
set<int>::iterator s_end = s0.end();
s_begin++; s_begin++;//begin + 2
s_end--; s_end--;//end - 2
s0.erase(s_begin, s_end);
cout << "s0 : "; printSet(s0);//s0 = 0 2 8 9

//清空全部元素
s0.clear();
cout << "s0 : "; printSet(s0);//s0 = EMPTY

         程序运行结果如下所示:

4.7 set查找与统计指定元素

声明解释
find(T elem)

找到指向第一个elem元素的迭代器

(如果没有找到,则返回end())

count(T elem)

统计值为elem的元素个数

multiset<int> ms0;
for (int i = 0; i < 10; i++) {
	ms0.insert(i); 
	ms0.insert(i);
}//s0 = 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 

cout << "s0 : "; printSet(ms0);

multiset<int>::iterator it = ms0.find(7);
cout << *it << endl;//7
cout << "ms0.find(5) : " << ms0.count(5) << endl;//2

         程序的执行结果如下所示:

4.8 set排序

4.8.1 内置类型的set排序

        set容器默认的排序规则:从小到大。可以使用仿函数改变排序规则。

        仿函数可能是一个类或者结构体,重载了函数调用运算符operator(),使得对象可以函数一样被调用。它本质上就是一个只重载了operator( ) 的类,它的出现是为了代替C语言中的函数指针。下面用一个例子具体表现仿函数set内置排序中的应用:

//打印set集合的所有元素
template<typename T,typename ELEM_T>
void printSet(const set<ELEM_T, T>& s) {
	if (s.empty()) {
		cout << "This set is EMPTY now." << endl;

		return;
	}

	for (ELEM_T elem : s) {
		cout << elem << ' ';
	}
	cout << endl;
}

//仿函数:重载了()运算符的类
class MySetCompare {
public :
	//重载()运算符
	bool operator()(int a, int b) const{
		return a > b;//降序
	}
};

int main() {
	set<int, MySetCompare> ms0;
	for (int i = 0; i < 10; i++) {
		ms0.insert(i);
	}//s0 = 9 8 7 6 5 4 3 2 1 0  
	cout << "s0 : "; printSet(ms0);

	return 0;
}

  4.8.2 自定义数据类型set排序

        和上边内置类型类似,我们只需要通过仿函数设置自己的排序规则即可。以下例子为了便于说明问题,将成员均设置为public

//士兵类:排名规则,先按年龄升序,再按身高降序
class Soldier {
public:
	string name;//姓名
	int age;//年龄
	int height;//身高

	Soldier(string n, int a, int h) : name(n), age(a), height(h) {}
};

//重载<<运算符
ostream& operator<< (ostream& cout, Soldier &s) {
	cout << s.name << "今年" << s.age << "岁,身高" << s.height << "cm。";

	return cout;
}

//仿函数:比较规则
class SoldierCompare {
public:
	bool operator()(Soldier s0, Soldier s1) const {
		if (s0.age == s1.age) {
			return s0.height > s1.height;
		}
		else {
			return s0.age < s1.age;
		}
	}
};

//打印set集合的所有元素
void printSet(const set<Soldier, SoldierCompare>& s) {
	if (s.empty()) {
		cout << "This set is EMPTY now." << endl;

		return;
	}

	for (Soldier elem : s) {
		cout << elem << endl;
	}

	cout << endl;
}

int main() {
	set<Soldier, SoldierCompare> soldiers;

	//添加士兵
	Soldier A("A", 30, 180);
	Soldier B("B", 18, 189);
	Soldier C("C", 35, 165);
	Soldier D("D", 18, 175);
	Soldier E("E", 35, 170);
	Soldier F("F", 25, 180);
	soldiers.insert(A);
	soldiers.insert(B);
	soldiers.insert(C);
	soldiers.insert(D);
	soldiers.insert(E);
	soldiers.insert(F);

	cout << "soldiers : " << endl;
	printSet(soldiers);
	
	return 0;
}

        上述例子中的程序运行结果如下所示:

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

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

相关文章

Windows环境安装Redis和Redis Desktop Manager图文详解教程

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl Redis概述 Redis是一个开源的高性能键值对数据库&#xff0c;以其卓越的读写速度而著称&#xff0c;广泛用于数据库、缓存和消息代理。它主要将数据存储在内存中&#xff0…

CISC和RISC指令集

文章目录 1. 指令集 2. CISC&#xff08;复杂指令集计算&#xff09; 3. RISC&#xff08;精简指令集计算&#xff09; 4. RISC的设计初衷 5. CISC和RISC流程对比 CISC&#xff08;复杂指令集计算&#xff09;的实现 RISC&#xff08;精简指令集计算&#xff09;的实现 …

【高中数学之函数】四种幂函数图线(二次、三次、开方、开立方)

【图像】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>UNASSIGNED</title><style type"text/css">.c…

【智能算法应用】灰狼算法求解二维栅格路径规划问题

目录 1.算法原理2.二维路径规划数学模型3.结果展示4.参考文献5.代码获取 1.算法原理 【智能算法】灰狼算法&#xff08;GWO&#xff09;原理及实现 2.二维路径规划数学模型 栅格法模型最早由 W.E. Howden 于 1968 年提出&#xff0c;障碍物的栅格用黑色表示&#xff0c;可通…

基于pi控制的数字锁相环simulink建模与仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 2.算法运行软件版本 matlab2022a 3.部分核心程序 &#xff08;完整版代码包含详细中文注释和操作步骤视频&#xff09…

基于MATLAB的PEF湍流风场生成器模拟与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于MATLAB的PEF湍流风场生成器模拟与仿真。PEF&#xff08;Primitive Equations Formulation&#xff09;湍流风场模型&#xff0c;是大气科学和气象学中用来描述大气流动和气…

咬文嚼字:词元是当今生成式人工智能失败的一个重要原因

生成式人工智能模型处理文本的方式与人类不同。了解它们基于"标记"的内部环境可能有助于解释它们的一些奇怪行为和顽固的局限性。从 Gemma 这样的小型设备上模型到 OpenAI 业界领先的 GPT-4o 模型&#xff0c;大多数模型都建立在一种称为转换器的架构上。由于转换器在…

subset使用

在R语言中&#xff0c;subset()函数用于从数据框中选择满足特定条件的观测。其语法如下&#xff1a; subset(x, subset, select, drop FALSE) 参数说明&#xff1a; x&#xff1a;数据框或矩阵。 subset&#xff1a;逻辑条件&#xff0c;用于筛选满足特定条件的行。 select…

Linux Bridge - Part 2

概览 在前一篇文章中&#xff0c;我描述了Linux 网桥&#xff08;bridge&#xff09;的配置&#xff0c;并展示了一个实验&#xff0c;其中使用Wireshark来分析流量。在本文中&#xff0c;我将讨论当创建一个网桥时会发生什么&#xff0c;以及Linux 网桥&#xff08;bridge&am…

给您介绍工控CAN总线

CAN是什么 CAN&#xff0c;全称Controller Area Network&#xff0c;即控制器局域网&#xff0c;是一种由Bosch公司在1983年开发的通信协议。它主要用于汽车和工业环境中的电子设备之间的通信。CAN协议定义了物理层和数据链路层的通信机制&#xff0c;使得不同的设备能够通过CA…

数据驱动的内容优化:Kompas.ai如何提升内容表现

在数字化营销时代&#xff0c;内容是企业与用户沟通的重要桥梁。然而&#xff0c;随着信息量的爆炸性增长&#xff0c;如何让内容在激烈的竞争中脱颖而出&#xff0c;成为每个营销人员面临的问题。数据驱动的内容优化策略&#xff0c;通过精准分析和科学决策&#xff0c;帮助品…

基于Java+SpringMvc+Vue技术的实验室管理系统设计与实现

博主介绍&#xff1a;硕士研究生&#xff0c;专注于信息化技术领域开发与管理&#xff0c;会使用java、标准c/c等开发语言&#xff0c;以及毕业项目实战✌ 从事基于java BS架构、CS架构、c/c 编程工作近16年&#xff0c;拥有近12年的管理工作经验&#xff0c;拥有较丰富的技术架…

基于Transformer的端到端的目标检测 | 读论文

本文正在参加 人工智能创作者扶持计划 提及到计算机视觉的目标检测&#xff0c;我们一般会最先想到卷积神经网络&#xff08;CNN&#xff09;&#xff0c;因为这算是目标检测领域的开山之作了&#xff0c;在很长的一段时间里人们都折服于卷积神经网络在图像处理领域的优势&…

SQLite 嵌入式数据库

目录&#xff1a; 一、SQLite 简介二、SQLite 数据库安装1、安装方式一&#xff1a;2、安装方式二&#xff1a; 三、SQLite 的命令用法1、创建、打开、退出数据库&#xff1a;2、编辑数据库&#xff1a; 四、SQLite 的编程操作1、打开 / 创建数据库的 C 接口&#xff1a;2、操作…

欧拉函数.

性质1&#xff1a;质数n的欧拉函数为n-1. 性质2&#xff1a;如果p&#xff0c;q都是质数&#xff0c;那么ϕ ( p ∗ q ) ϕ ( p ) ∗ ϕ ( q ) ( p − 1 ) ∗ ( q − 1 ) 证明&#xff1a;p&#xff0c;2p....q*p都不与q*p互质&#xff0c;q同理&#xff0c;所以总的不互质个…

WPS+Python爬取百度之星排名

运行效果 手动拉取 https://www.matiji.net/exam/contest/contestdetail/146 如果手动查找&#xff0c;那么只能通过翻页的方式&#xff0c;每页10行&#xff08;外加一行自己&#xff09;。 爬取效果预览 本脚本爬取了个人排名和高校排名&#xff0c;可以借助WPS或MS Offi…

专业140+总分420+天津大学815信号与系统考研经验天大电子信息与通信工程,真题,大纲,参考书。

顺利上岸天津大学&#xff0c;专业课815信号与系统140&#xff0c;总分420&#xff0c;总结一些自己的复习经历&#xff0c;希望对于报考天大的同学有些许帮助&#xff0c;少走弯路&#xff0c;顺利上岸。专业课&#xff1a; 815信号与系统&#xff1a;指定教材吴大正&#xf…

缺失行处理(R和python)

R(complete.cases) rm(listls()) # 创建一个包含缺失值的数据框 # df <- data.frame( # x c(1, 2, NA, 4), # y c(NA, 2, 3, 4), # z c(1, NA, 3, 3) # ) # # # 使用complete.cases函数筛选包含缺失值的数据行 # missing_rows <- !complete.cases(df) # # # …

Vue2前端实现数据可视化大屏全局自适应 Vue实现所有页面自适应 Vue实现自适应所有屏幕

Vue自适应所有屏幕大小,目前页面自适应,尤其是数据可视化大屏的自适应更是案例很多 今天就记录一下使用Vue全局自适应各种屏幕大小的功能 在Vue.js中创建一个数据大屏,并使其能够自适应不同屏幕大小,通常涉及到布局的响应式设计、CSS媒体查询、以及利用Vue的事件系统来处理…

C++面向对象的常见面试题目(一)

1. 面向对象的三大特征 &#xff08;1&#xff09;封装&#xff1a;隐藏对象的内部状态&#xff0c;只暴露必要的接口。 #include <iostream> #include <string>// 定义一个简单的类 Person class Person { private: // 私有成员&#xff0c;外部不可直接访问std…