set 用法整理

封面图

应用场景

set顾名思义是一个集合。数学中集合具有无序性和互异性,也就是集合中元素互不相同没有顺序。而在C++中,set不同于vector是顺序容器,它是一个关联容器,是一个内部自动有序(升序)、自动去重的容器。内部实现是基于红黑树,一种自平衡的二叉搜索树。

根据set的特点,当我们需要去重或者按照关键字排序时,可以考虑使用set

使用

定义

跟其他的容器类差不多,先是包含头文件set,然后确定元素类型

#include <set>
using namespace std;

void test_set() {
	set<int> S; // 元素类是是int
}

find(value) 查找对应值为value的迭代器,如果不存在返回end()末迭代器
迭代器很不方便
count(value) 返回值等于value的元素个数(0 或 1)

除了上面两种方法,c++20 引入了一个新方法 contains(value)
学过java的朋友应该很熟悉,Set接口里有一个抽象方法就是contains,名字完全一样:)作用也完全一样。查找集合中是否存在值为value的元素,返回值是布尔类型。

insert()
insert(val)可将val插入set容器中,并自动递增排序和去重,时间复杂度O(logn)

	void test_set() {
	set<int> S;
	
	S.insert(1);
	S.insert(2);
	S.insert(1); // set中存在1,拒绝插入
	for(int num : S) printf("%d ",num); // 打印输出 1 2 递增排序
}

erase()
形式参数有两种:迭代器和值。

  1. erase(it) 形式参数为待删元素的迭代器

  2. erase(val) 形式参数为待删元素的值

遍历

迭代器
	set<int> S;
	// 插入操作
	for(set<int>::iterator::it = S.begin(); it != S.end(); it++) {
		printf("%d ",*it);
	}
	// C++11 可以使用auto关键字
	for(auto it = S.begin(); it != S.end(); it++) {
		printf("%d ",*it);
	}
范围for循环(for-range)
	set<int> S;
	// 插入操作
	for(int num : S) {
		printf("%d ",num);
	}
	// 没错就是这么简单

其他成员函数

size()

vector相同,返回set容器的大小

clear()

清空容器


存储自定义类型

set是自动根据键值排序的,所以当我们要存储自定义类型(structclass)时该怎么办呢?
我只介绍一种比较熟悉的方法——<重载。除此之外,还有仿函数等方法。

设想这样的情景:一次期末考试,有n名同学参加。现在有若干名同学的学号和对应分数(整数)。需求是按照分数递减的顺序打印各位同学的学号及其对应分数。假设学号是字符串类型,并且分数互不相同。

  1. 定义结构体,重载运算符 <
  2. 定义set
  3. 录入各学生学号和成绩,放到set中存储
  4. 遍历set,降序打印学号
#include <iostream>
#include <set>
using namespace std;

struct Student {
	string id;
	int score;
	Student(string _id,int _score) : id(_id),score(_score) {
	}
	
	bool operator<(const Student &other) const {
		return this->score > other.score;
	}
	/* 注意两个点:
		1. 需要定义为常函数
		2. 默认是升序,只需将 < 重载为 > 既可以达到降序结果
	*/
};

int main() {
	int n;
	scanf("%d",&n);
	set<Student> S;
	string id;
	int score;
	for(int i = 0; i < n; i++) {
		cin>>id;
		cin>>score;
		S.insert(Student(id,score));
	}
	// 范围for循环写起来很简单
	for(Student student:S) {
		cout<<student.id<<" "<<student.score<<"\n";
	}
    return 0;
}

这里要求的是分数不相同,但是现实情况是存在并列的情况的,那我们该怎么办?

解决办法——multiset

multiset中文名字是多重集,与set的唯一不同是不会自动去重,仍按照键值排序,头文件还是set。所以上面的代码只需要修改定义set的那行代码

int main() {
	int n;
	scanf("%d",&n);
	multiset<Student> S;
	// ...
    return 0;
}

例题

这道例题利用的是set的自动去重

LeetCode 136传送门🔗

分析

  c = 2 ∗ ( a + b + c ) − ( a + a + b + c + b ) . \ c = 2 * (a +b +c) - (a +a+b+c+b).  c=2(a+b+c)(a+a+b+c+b).

思路就是将数组中元素去重然后求和乘2减掉原数组元素之和,结果就是只出现一次的数字。

// link:https://leetcode-cn.com/problems/single-number
// author: zyoung
class Solution {
int sum(vector<int> nums) {
	int result = 0;
	for(int num : nums) result += num;
	return result;
}

int sum(set<int> S) {
	int result = 0;
	for(int num : S) result += num;
	return result;
}
public:
    int singleNumber(vector<int>& nums) {
        if(1 == (int)nums.size()) return nums[0];

        set<int> S;
        for(int num:nums) S.insert(num);
        int sum_vector =  this->sum(nums);
        int sum_set = this->sum(S);
		return sum_set * 2 - sum_vector;
    }
};

需要注意的是,这种思路在数字比较大的情况下可能会溢出,所以不推荐使用该方法,这里仅作为set例题展示。

好文推荐
vector用法整理
leetcode 231题解

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值