C++笔记 STL——set

在 C++ 的标准模板库(STL)中,set 是一个核心的关联式容器,它以自动排序元素唯一性 为核心特性,为开发者提供了高效的元素存储、查找与管理方案。不同于数组、vector 这类顺序容器,set 不关注元素的插入顺序,而是专注于元素的有序性和唯一性,是处理去重、排序、快速查找等场景的理想工具。

本文将从核心特性、底层原理、基础用法、常用操作、适用场景及注意事项等方面,全面讲解 STL 中的 set

一、set 的核心特性

  1. 元素唯一性set 中不允许存储重复元素,插入重复值时,容器会自动忽略该操作,保证容器内元素无重复。
  2. 自动排序set 默认按照升序对元素进行排序(可自定义排序规则),无需手动调用排序算法。
  3. 不可修改元素set 中的元素本质是常量,不能直接修改元素值(修改会破坏排序规则),若需修改,必须先删除旧元素,再插入新元素。
  4. 高效操作:基于平衡二叉树(红黑树)实现,查找、插入、删除操作的时间复杂度均为 \(O(log n)\),远优于顺序容器的线性查找。

二、set 的底层原理

set 的底层实现是红黑树 (一种自平衡的二叉查找树)。红黑树的特性保证了 set 始终保持有序,且能在对数时间内完成核心操作:

自平衡:避免树结构退化,保证操作效率稳定;

有序性:树的中序遍历结果即为元素的排序结果,天然支持有序遍历。

这也是 set 能实现自动排序和高效操作的核心原因。

三、set 的基础用法

使用 set 必须包含头文件 <set>,且属于 std 命名空间,基础代码框架如下:

1. 头文件与命名空间

cpp 复制代码
#include <iostream>
#include <set>  // 必须包含的头文件
using namespace std;

2. 定义与初始化

set 是模板类,需指定存储元素的类型,支持多种初始化方式:

cpp 复制代码
// 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,1

3. 插入元素

使用 insert() 方法插入元素,支持单个插入、批量插入:

cpp 复制代码
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 遍历两种常用方式:

cpp 复制代码
// 方法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() 返回指向最后一个元素下一个位置的迭代器

示例:核心操作实战

cpp 复制代码
#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 的特性决定了它的适用场景,核心用于:

  1. 自动去重:无需手动判断重复,直接插入即可保证元素唯一;
  2. 有序存储:需要数据始终保持有序,无需手动排序;
  3. 快速查找:频繁判断元素是否存在,效率远高于 vector;
  4. 去重 + 排序:同时需要去重和排序的场景(如统计不重复的分数、编号等)。

场景举例

统计学生的唯一学号;

过滤重复的文本关键词;

有序存储排行榜数据;

快速判断某个值是否在集合中。

六、set 与 multiset、unordered_set 的区别

STL 中还有两个与 set 相似的容器,三者对比如下:

  1. set:元素唯一,自动排序,底层红黑树;
  2. multiset允许重复元素,自动排序,底层红黑树;
  3. unordered_set :元素唯一,不排序,底层哈希表,查找速度更快(\(O(1)\))。

根据业务需求选择:需要有序选 set,允许重复选 multiset,追求极致查找效率选 unordered_set

七、注意事项

  1. 不能直接修改 set 中的元素,必须先删后插;
  2. 插入自定义类型时,需要重载 < 运算符(否则 set 无法排序);
  3. 迭代器遍历的结果是有序的,不要依赖插入顺序;
  4. count() 函数仅返回 0 或 1,用于判断元素存在即可。

总结

STL 中的 set 是一个有序、唯一、高效的关联式容器,依托红黑树实现了 \(O(log n)\) 级别的核心操作,完美适配去重、排序、快速查找等场景。它的用法简洁、功能强大,是 C++ 开发中处理集合类数据的首选工具之一。

掌握 set 的用法,能大幅简化代码逻辑,提升程序效率,是学习 STL 容器的重要一环。

相关推荐
L1624762 小时前
Win11 共享→Windows Server 访问故障总结(极简可复用)
开发语言·windows·php
d111111111d3 小时前
直流电机位置式 PID 控制 和 舵机的区别
笔记·stm32·单片机·嵌入式硬件·学习
dgaf3 小时前
DX12 快速教程(17) —— 立体图标与合并渲染
c语言·c++·3d·图形渲染·d3d12
LZYmarks3 小时前
小白买车笔记
笔记
.柒宇.3 小时前
FastAPI 基础指南:从入门到实战
开发语言·python·fastapi
xyq20243 小时前
Go 错误处理
开发语言
码途漫谈3 小时前
Easy-Vibe开发篇阅读笔记(二)——前端开发之Figma与MasterGo入门
人工智能·笔记·ai·开源·ai编程·figma
JAVA面经实录9173 小时前
企业级java+LangChain4j-RAG系统 限流熔断降级
java·开发语言·分布式·langchain
Slow菜鸟4 小时前
Codex CLI 教程(五)| AI 驱动项目从零到一:面向 Java 全栈工程师打造个人 ECC(V2版)
java·开发语言·人工智能