C++学习笔记——STL【容器:set与multiset】

本文详细介绍了C++标准库中的set和multiset容器,包括它们的定义、特性(如排序规则、元素唯一性)、构造函数、大小与交换、元素插入与删除、查找与统计,以及set与multiset的主要区别。通过实例演示了如何使用和自定义排序规则。
摘要由CSDN通过智能技术生成

基本概念

set与multiset 容器定义于头文件,并位于 std 命名空间中。

set 集合容器,属于关联式容器,内部数据结构为二叉树,有以下几个特性:

  • set 容器专门用于存储键和值相等的键值对,因此该容器中真正存储的是各个键值对的值(value)
  • set 容器在存储数据时,会根据各元素值的大小对存储的元素进行排序(默认做升序排序);
  • 存储到 set 容器中的元素,虽然其类型没有明确用 const 修饰,但正常情况下它们的值是无法被修改的;
  • set 容器存储的元素不能产重复。

multiset 容器,与set容器唯一的区别,就是 multiset 允许元素重复。

构造函数

功能描述:创建set容器以及赋值

构造:

  • set<T> st; //默认构造函数:
  • set(const set &st); //拷贝构造函数

赋值:

  • set& operator=(const set &st); //重载等号操作符
#include <iostream>
#include<string>
#include<set>

using namespace std;

template<class T>
void print_set(const T& myset) {
	for (set<int>::const_iterator p = myset.begin(); p != myset.end(); p++) {
		cout << *p << " ";
	}
	cout << endl;
}

/*构造函数*/
void test41_1() {

	/*默认无参构造函数*/

// set 容器,数据插入自动排序,元素不能有重复
// set 容器插入数据只有 insert 方法
	set<int> s1;
	s1.insert(5);
	s1.insert(1);
	s1.insert(3);
	s1.insert(2);
	s1.insert(5);
	print_set(s1); // 1 2 3 5
	// multiset 容器,数据插入自动排序,元素可以重复
	multiset<int> ms1;
	ms1.insert(5);
	ms1.insert(1);
	ms1.insert(3);
	ms1.insert(2);
	ms1.insert(5);
	print_set(ms1); // 1 2 3 5 5

	/*拷贝构造函数*/
	set<int> s2(s1);
	print_set(s2); // 1 2 3 5 

	multiset<int> ms2(ms1);
	print_set(ms2); // 1 2 3 5 5
}

int main() {
	test41_1();
	return 0;
}

容器大小与元素交换操作

功能描述:

  • 统计set容器大小以及交换set容器

函数原型:

  • size(); //返回容器中元素的数目
  • empty(); //判断容器是否为空
  • swap(st); //交换两个集合容器

set容器中不能重置(resize)容器大小。

/*set容器的大小与元素交换*/
void test41_2() {
	set<int> s1;
	s1.insert(5);
	s1.insert(7);
	s1.insert(4);
	s1.insert(1);
	print_set(s1); // 1 4 5 7
	set<int> s2;
	s2.insert(10);
	s2.insert(14);
	s2.insert(8);
	print_set(s2); // 8 10 14

	/*获取set容器中的元素个数*/
	cout << "s1大小:" << s1.size() << endl; // s1大小:4
	
	/*判断set容器是否为空*/
	cout << "s1是否为空:" << (s1.empty() ? "为空" : "不为空") << endl; // s1是否为空:不为空
	
	/*交换s1与s2中的元素*/
	s1.swap(s2);
	print_set(s1); // 8 10 14
	print_set(s2); // 1 4 5 7
	
}

元素插入与删除

功能描述:

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

函数原型:

  • insert(elem); //在容器中插入元素。
  • clear(); //清除所有元素
  • erase(pos); //删除pos迭代器所指的元素,返回下一个元素的迭代器。
  • erase(beg, end); //删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。
  • erase(elem); //删除容器中值为elem的元素。
void test41_3() {
	set<int> s;
	/*只能使用insert函数插入元素*/
	s.insert(7);
	s.insert(1);
	s.insert(3);
	s.insert(2);
	s.insert(4);
	print_set(s); // 1 2 3 4 7

	/*删除迭代器指向位置的元素*/
	// 只读迭代器:指向第一个元素 1
	set<int>::const_iterator p = s.begin(); 
	p++; // 向后移动一个位置,指向元素 2
	cout << *p << endl; // 2
	// 删除当前执行的元素 2,返回指向下一个元素 3 的迭代器
	set<int>::const_iterator p_next = s.erase(p); 
	print_set(s); // 1 3 4 7
	cout << *p_next << endl; // 3
	// 原本p指向的元素被删除后,对应的迭代器p也就失效,不能再使用
	// cout << *p << endl; // 运行报错

	cout << "********************" << endl;

	/*删除迭代器指向区间内的元素*/
	set<int> s2;
	s2.insert(7);
	s2.insert(1);
	s2.insert(3);
	s2.insert(2);
	s2.insert(4);
	print_set(s2); // 1 2 3 4 7
	set<int>::const_iterator p_start = s2.begin(); // 指向第1个元素 1
	cout << *p_start << endl; // 1
	p_start++; // 指向第二个元素 2
	cout << *p_start << endl; // 2
	set<int>::const_iterator p_end = s2.end(); // 指向最后一个元素7的下一个位置
	p_end--; // 指向最后一个元素 7
	cout << *p_end << endl; // 7
	// 删除 [p_start,p_end) 区间内的元素:2 3 4
	s2.erase(p_start, p_end);
	print_set(s2); // 1 7

	/*根据值删除元素*/
	s2.erase(1); // 删除值为 1 的元素
	print_set(s2); // 7
	
	/*清空容器*/
	s2.clear();
	print_set(s2); 
}

元素查找与统计

功能描述:

  • 对set容器进行查找数据以及统计数据

函数原型:

  • find(key); //查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();
  • count(key); //统计key的元素个数
void test41_4() {
	set<int> s;
	s.insert(7);
	s.insert(1);
	s.insert(3);
	s.insert(2);
	s.insert(4);
	print_set(s); // 1 2 3 4 7

	/*查找某个元素,找到返回指向元素的迭代器,没有找到就返回指向容器最后一个元素下一个位置的迭代器*/
	// 查找 4
	set<int>::iterator p1 = s.find(4);
	if (p1 == s.end()) {
		cout << "未找到" << endl;
	}
	else {
		cout << "找到了:" << *p1 << endl; // 找到了:4
	}
	// 查找20
	set<int>::iterator p2 = s.find(20);
	if (p2 == s.end()) {
		cout << "未找到" << endl; // 未找到
	}
	else {
		cout << "找到了:" << *p2 << endl; 
	}

	/*统计某个元素出现的次数 set 集合的统计结果只能是 0或者 1*/
	cout << s.count(4) << endl; // 1
	cout << s.count(20) << endl; // 0

	multiset<int> ms;
	ms.insert(2);
	ms.insert(1);
	ms.insert(2);
	ms.insert(5);
	ms.insert(1);
	ms.insert(4);
	ms.insert(3);
	print_set(ms); // 1 1 2 2 3 4 5
	cout << ms.count(2) << endl; // 2 
	cout << ms.count(1) << endl; // 2
	cout << ms.count(3) << endl; // 1
}

set 和 multiset 的区别

区别:

  • set不可以插入重复数据,而multiset可以
  • set插入数据的同时会返回插入结果,表示插入是否成功
  • multiset不会检测数据,因此可以插入重复数据
void test41_5(){
	set<int> s1;
	pair<set<int>::iterator,bool> result = s1.insert(1);
	// 判断是否插入成功
	if (result.second) {
		// 通过返回的迭代器获取插入成功的元素
		cout << "插入成功:" << *result.first << endl;
	}
	else {
		cout << "插入失败" << endl;
	}
	pair<set<int>::iterator,bool> result2 = s1.insert(1);
	if (result2.second) {
		cout << "插入成功:" << *result2.first << endl;
	}
	else {
		cout << "插入失败" << endl;
	}
}

pair 对组

功能描述:

  • 成对出现的数据,利用对组可以返回两个数据

两种创建方式:

  • pair<type, type> p ( value1, value2 );
  • pair<type, type> p = make_pair( value1, value2 );
void test41_6() {
	/*创建对组 方式1*/
	pair<string, int> p1("张三", 25);
	// 获取对组中的第一个值
	cout << "第一个值,姓名:" << p1.first << endl; // 第一个值,姓名:张三
	// 获取对组中的第二个值
	cout << "第二个值,年龄:" << p1.second << endl; // 第二个值,年龄:25

	/*创建对组 方式2*/
	pair<string, int> p2 = make_pair("李四",26);
	// 获取对组中的第一个值
	cout << "第一个值,姓名:" << p2.first << endl; // 第一个值,姓名:李四
	// 获取对组中的第二个值
	cout << "第二个值,年龄:" << p2.second << endl; // 第二个值,年龄:26
}

set 容器排序规则

set容器默认排序规则为从小到大,利用仿函数,可以改变排序规则。

set 容器存放内置基本数据类型

利用仿函数可以指定set容器的排序规则。
自定义一个比较器 MyIntCompare 类,重载 operator()操作符,函数体返回两个 int 值比较的结果用于排序。如下:

#include <iostream>
#include <string>
#include <set>

using namespace std;

class MyIntCompare {
public:
	// 重载小括号操作符
	bool operator()(int v1, int v2){
		// 降序
		return v1 > v2;
	}
};

int main() {
	set<int> s1;
	s1.insert(5);
	s1.insert(1);
	s1.insert(4);
	s1.insert(2);
	s1.insert(3);
	// int 类型 默认升序排序
	for (set<int>::const_iterator p = s1.begin(); p != s1.end(); p++) {
		cout << *p << " ";
	}
	cout << endl; // 1 2 3 4 5

	// 构建set时,指定排序规则
	set<int,MyIntCompare> s2;
	s2.insert(5);
	s2.insert(1);
	s2.insert(4);
	s2.insert(2);
	s2.insert(3);
	for (set<int, MyIntCompare>::const_iterator p2 = s2.begin(); p2 != s2.end(); p2++) {
		cout << *p2 << " ";
	}
	cout << endl; 
	return 0;
}

程序编译失败,报错:

1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\include\xutility(1455,1): 
error C3848: 具有类型“const MyIntCompare”的表达式会丢失一些 const-volatile 限定符以调用“bool MyIntCompare::operator ()(int,int)”

参考
volatile影响编译器编译的结果,指volatile 变量是随时可能发生变化的,与volatile变量有关的运算,不要进行编译优化,以免出错,(VC++ 在产生release版可执行码时会进行编译优化,加volatile关键字的变量有关的运算,将不进行编译优化。)

volatile 告诉编译器i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的可执行码会重新从i的地址读取数据放在k中。而优化做法是,由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在k中。而不是重新从i里面读

报错内容大概意思为:传入的参数表达式应具有“const myCompare”类型,而你调用的“bool myCompare::operator ()(int,int)”不具备const属性,会丢失const限定,所以无法通过编译。

故只要将operator()具有const属性便可,如下

#include <iostream>
#include <string>
#include <set>

using namespace std;

class MyIntCompare {
public:
	// 重载小括号操作符
	// 
	bool operator()(int v1, int v2) const{
		// 降序
		return v1 > v2;
	}
};

int main() {
	set<int> s1;
	s1.insert(5);
	s1.insert(1);
	s1.insert(4);
	s1.insert(2);
	s1.insert(3);
	// int 类型 默认升序排序
	for (set<int>::const_iterator p = s1.begin(); p != s1.end(); p++) {
		cout << *p << " ";
	}
	cout << endl; // 1 2 3 4 5

	// 构建set时,指定排序规则
	set<int,MyIntCompare> s2;
	s2.insert(5);
	s2.insert(1);
	s2.insert(4);
	s2.insert(2);
	s2.insert(3);
	for (set<int, MyIntCompare>::const_iterator p2 = s2.begin(); p2 != s2.end(); p2++) {
		cout << *p2 << " ";
	}
	cout << endl; // 5 4 3 2 1

	return 0;
}

set容器存放自定义数据类型

对于自定义数据类型,set构建时必须指定排序规则才可以插入数据。

#include <iostream>
#include <string>
#include <set>

using namespace std;

class Cat {
public:
	string name;
	int age;
	Cat(string name,int age) {
		this->name = name;
		this->age = age;
	}
};
// Cat 比较器
class CatCompare {
public:
	bool operator()(const Cat& c1, const Cat& c2) const {
		// 根据年龄升序排序
		return c1.age < c2.age;
	}

};

// 重载左移运算符
ostream& operator<<(ostream& out, const Cat& cat) {
	out << "姓名:" << cat.name << ",年龄:" << cat.age;
	return out;
}

int main() {
	Cat c1("红猫", 13);
	Cat c2("蓝猫", 9);
	Cat c3("黑猫", 8);
	Cat c4("橘猫", 11);
	// 构建set容器时,没有传入对应的比较规则,无法插入数据
	set<Cat> s1;
	// 报错: error C2676: 二进制“<”:“const _Ty”不定义该运算符或到预定义运算符可接收的类型的转换
	s1.insert(c1);

	return 0;
}

在构建 set 容器时,传入指定的排序规则

int main() {
	Cat c1("红猫", 13);
	Cat c2("蓝猫", 9);
	Cat c3("黑猫", 8);
	Cat c4("橘猫", 11);
	// 构建set容器时,传入对应的比较规则
	set<Cat,CatCompare> s1;

	s1.insert(c1);
	s1.insert(c2);
	s1.insert(c3);
	s1.insert(c4);
	
	for (set<Cat>::const_iterator p = s1.begin(); p != s1.end(); p++) {
		cout << *p << endl;
	}
	/*
	姓名:黑猫,年龄:8
	姓名:蓝猫,年龄:9
	姓名:橘猫,年龄:11
	姓名:红猫,年龄:13
	*/

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值