C++笔记 STL——set
在 C++ 的标准模板库(STL)中,set是一个核心的关联式容器,它以自动排序和元素唯一性为核心特性,为开发者提供了高效的元素存储、查找与管理方案。不同于数组、vector 这类顺序容器,set不关注元素的插入顺序,而是专注于元素的有序性和唯一性,是处理去重、排序、快速查找等场景的理想工具。
本文将从核心特性、底层原理、基础用法、常用操作、适用场景及注意事项等方面,全面讲解 STL 中的set。
一、set 的核心特性
- 元素唯一性:
set中不允许存储重复元素,插入重复值时,容器会自动忽略该操作,保证容器内元素无重复。 - 自动排序:
set默认按照升序对元素进行排序(可自定义排序规则),无需手动调用排序算法。 - 不可修改元素:
set中的元素本质是常量,不能直接修改元素值(修改会破坏排序规则),若需修改,必须先删除旧元素,再插入新元素。 - 高效操作:基于平衡二叉树(红黑树)实现,查找、插入、删除操作的时间复杂度均为 \(O(log n)\),远优于顺序容器的线性查找。
二、set 的底层原理
set的底层实现是红黑树(一种自平衡的二叉查找树)。红黑树的特性保证了set始终保持有序,且能在对数时间内完成核心操作:
自平衡:避免树结构退化,保证操作效率稳定;
有序性:树的中序遍历结果即为元素的排序结果,天然支持有序遍历。
这也是set能实现自动排序和高效操作的核心原因。
三、set 的基础用法
使用set必须包含头文件<set>,且属于std命名空间,基础代码框架如下:
1. 头文件与命名空间
#include <iostream> #include <set> // 必须包含的头文件 using namespace std;2. 定义与初始化
set是模板类,需指定存储元素的类型,支持多种初始化方式:
// 1. 空 set 定义 set<int> s1; // 2. 列表初始化(C++11及以上) set<int> s2 = {3, 1, 4, 1, 5}; // 重复元素1会被自动去重,最终元素:1,3,4,5 // 3. 拷贝初始化 set<int> s3(s2); // 4. 自定义排序规则(降序) set<int, greater<int>> s4 = {3, 1, 4}; // 元素:4,3,13. 插入元素
使用insert()方法插入元素,支持单个插入、批量插入:
set<int> s; // 单个插入 s.insert(2); s.insert(1); s.insert(3); // 插入重复元素,自动忽略 s.insert(2); // 批量插入 int arr[] = {5, 4}; s.insert(arr, arr + 2);4. 遍历元素
set支持迭代器遍历、范围 for 遍历两种常用方式:
// 方法1:迭代器遍历 set<int>::iterator it; for (it = s.begin(); it != s.end(); it++) { cout << *it << " "; // 输出:1 2 3 4 5 } // 方法2:范围for遍历(更简洁,C++11及以上) for (int num : s) { cout << num << " "; }四、set 的常用成员函数
set提供了丰富的成员函数,覆盖增删改查、容量判断等核心操作,常用函数如下:
表格
| 函数 | 功能 |
|---|---|
insert(val) | 插入元素val,自动去重 + 排序 |
erase(val) | 删除值为val的元素 |
erase(it) | 删除迭代器it指向的元素 |
find(val) | 查找元素val,返回指向该元素的迭代器;未找到则返回end() |
count(val) | 统计元素val的个数(结果只能是 0 或 1,用于判断元素是否存在) |
size() | 返回容器中元素的个数 |
empty() | 判断容器是否为空,空返回true,否则返回false |
clear() | 清空容器中所有元素 |
begin() | 返回指向第一个元素的迭代器 |
end() | 返回指向最后一个元素下一个位置的迭代器 |
示例:核心操作实战
#include <iostream> #include <set> using namespace std; int main() { set<int> s = {10, 20, 30}; // 1. 查找元素 auto it = s.find(20); if (it != s.end()) { cout << "找到元素:" << *it << endl; } else { cout << "未找到元素" << endl; } // 2. 判断元素是否存在 if (s.count(15)) { cout << "15存在" << endl; } else { cout << "15不存在" << endl; } // 3. 删除元素 s.erase(10); // 删除值为10的元素 // 4. 查看大小 cout << "元素个数:" << s.size() << endl; // 输出:2 // 5. 清空容器 s.clear(); cout << "是否为空:" << s.empty() << endl; // 输出:1(true) return 0; }五、set 的适用场景
set的特性决定了它的适用场景,核心用于:
- 自动去重:无需手动判断重复,直接插入即可保证元素唯一;
- 有序存储:需要数据始终保持有序,无需手动排序;
- 快速查找:频繁判断元素是否存在,效率远高于 vector;
- 去重 + 排序:同时需要去重和排序的场景(如统计不重复的分数、编号等)。
场景举例:
统计学生的唯一学号;
过滤重复的文本关键词;
有序存储排行榜数据;
快速判断某个值是否在集合中。
六、set 与 multiset、unordered_set 的区别
STL 中还有两个与set相似的容器,三者对比如下:
- set:元素唯一,自动排序,底层红黑树;
- multiset:允许重复元素,自动排序,底层红黑树;
- unordered_set:元素唯一,不排序,底层哈希表,查找速度更快(\(O(1)\))。
根据业务需求选择:需要有序选set,允许重复选multiset,追求极致查找效率选unordered_set。
七、注意事项
- 不能直接修改
set中的元素,必须先删后插; - 插入自定义类型时,需要重载
<运算符(否则set无法排序); - 迭代器遍历的结果是有序的,不要依赖插入顺序;
count()函数仅返回 0 或 1,用于判断元素存在即可。
总结
STL 中的set是一个有序、唯一、高效的关联式容器,依托红黑树实现了 \(O(log n)\) 级别的核心操作,完美适配去重、排序、快速查找等场景。它的用法简洁、功能强大,是 C++ 开发中处理集合类数据的首选工具之一。
掌握set的用法,能大幅简化代码逻辑,提升程序效率,是学习 STL 容器的重要一环。
