【C/C++】set集合容器

388 阅读5分钟

这是我的第一篇掘金博客,开启掘金写作之路。


set 集合容器简介

set 是我们常用的数据结构,用来存储同一数据类型,并提供常用的数据操作。

set 的基本特点

  • 头文件为 #include<set>
  • set 中的元素都是自动排好序的
  • set 集合中没有重复的元素,即元素的值都唯一
  • 只能通过迭代器 set<int>::iterator 访问集合元素

set中的元素是如何实现自动排序的呢?

set 集合容器实现了红黑树的平衡二叉检索树的数据结构,在插入元素时,会自动调整二叉树的排列,把该元素放到适当的位置,以确保每个子树根节点的键值大于左子树所有节点的键值,而小于右子树所有节点的键值;

这样存放的目的在于查找时能够快速检索,因为 set 在查找时使用的是二分查找,时间复杂度为 O(log2n)O(\log_2 n)

同时 set 集合还确保根节点的左子树的高度与右子树的高度平衡,这样,二叉树的高度最小,从而检索速度最快。需要注意的是,它不会重复插入相同键值的元素,而采取忽略处理。平衡二叉检索树的检索使用中序遍历算法,检索效率高于 vectordeque、和 list 的容器。

set常用的函数方法

函数方法函数功能
begin()返回指向第一个元素的迭代器
end()返回指向最后一个元素的迭代器
clear()清除所有元素
count()返回某个值元素的个数
empty()判断集合是否为空
erase()删除集合中的元素,删除的可以是指向的迭代器也可以是元素值
find()返回一个指向被查找到元素的迭代器
insert()在集合中插入元素
lower_bound()返回指向大于等于某值的第一个元素的迭代器,如果没找到,返回末尾的迭代器位置
upper_bound()返回大于某个值元素的迭代器,如果没找到,返回末尾的迭代器位置
max_size()返回集合能容纳的元素的最大限值
rbegin()返回指向集合中最后一个元素的反向迭代器
rend()返回指向集合中第一个元素的反向迭代器
size()集合中元素的数目
swap()交换两个集合变量

在什么情况使用 set 集合容器?

构造 set 集合的主要目的是为了快速检索。所以当我们需要对某个集合总的元素进行大量的查找、添加、删除操作时可以选择使用 set 集合容器。

这里需要注意的是 set 集合中的元素不能直接被修改。

set 集合使用案例分析:

题目链接:LCP 52. 二叉搜索树染色

题目大意: 对一颗二叉树上的节点值进行多次区间染色,返回最后染色情况。(这里只对题目做简单描述,题目完整描述可以点击链接查看,主要为了说明 set 集合的使用情况

示例:

image.png

输入:root = [4,2,7,1,null,5,null,null,null,null,6]
ops = [[0,2,2],[1,1,5],[0,4,5],[1,5,7]]
输出:5
解释:
第 0 次操作,将值为 2 的节点染蓝;
第 1 次操作,将值为 1245 的节点染红;
第 2 次操作,将值为 45 的节点染蓝;
第 3 次操作,将值为 567 的节点染红;
因此,最终值为 12567 的节点为红色节点,返回数量 5

解题思路分析:

  • 因为我们要对某区间进行多次修改和查询,常规做法是需要使用线段树进行完成,但是犹豫线段树代码非常冗长,查代码也比较困难,对于很多没有接触过线段树的人来说更是无法完成。
  • 但是该题还可以仅通过使用 set 集合来完成,因为每个点的颜色只取决于最后一次染色操作。所以我们可以用 set 维护所有颜色待确定的点,倒序处理询问,每次利用 lower_bound() 方法从 set 中取出所有在询问范围内的点染色后删掉。因为每个点只会被删掉一次,所以总体复杂度 O((n+q)logn)\mathcal{O}((n + q)\log n),其中 n 是节点数量,q 是询问数量。

小结: 该题通过巧妙的思维和灵活的使用了 set 集合以及内置函数 lower_bound() 方法,将看似无法解决的困难题目化解,这也充分体现了 set 集合的检索效率。

set 迭代器的声明方式以及使用

#include<bits/stdc++.h>
#include<set>
using namespace std;
int main(){
	//创建一个int类型的集合s
	set<int> s;
	//定义正向迭代器
	set<int>::iterator it;
	//中序遍历集合中的所有元素(从小到大)
	for(it = s.begin(); it != s.end(); it++) cout << *it << " ";
	//定义反向迭代器
	set<int>::reverse_iterator rit;
	//从大到小
	for(rit = s.rbegin(); rit != s.rend(); rit++) cout << *rit << " ";
	return 0;
}

注意: 使用反向迭代器reverse_iterator可以反向遍历集合,用到rbegin()rend()两个方法。

set 使用总结

  1. 判断 set 集合中某个元素是否存在时通常有两种操作可以实现:
    • 使用 find() 方法对集合进行检索,如果找到查找的的键值,则返回该键值的迭代器位置;否则,返回集合最后一个元素后面的一个位置,即 end()。通常判断语句为:
    if(s.find(x) != s.end());//set<int> s;
    
    • 也可以使用 count(x) 函数来判断集合中是否存在值为 x 的元素。(因为 set 集合不会重复插入,所以集合中每个元素的 count() 要么为 0 要么为 1
    if(s.count(x) == 1);//set<int> s;
    
  2. 判断 set 集合是否为空也有两种常用操作:
    • 使用 empty() 方法直接判断是否为空:
    if(s.empty());//set<int> s;
    
    • 使用 size() 方法判断集合中元素的数目是否为 0
    if(s.size() > 0);//set<int> s;
    

结束语

真正的失败,是你决定放弃的那一刻;这世上没有凭空而起的楼阁,也没有平白无故的成功;最好的时光,永远是现在。