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 容器的重要一环。

相关推荐
Lhan.zzZ3 分钟前
用 Qt 构建线程安全的通讯静态库:从线程亲和性到完整工程
c++·vscode·qt·tcp/ip·udp
后台模板学习7 分钟前
Go Web开发中任务调度框架的源码解析与高可用实现方案
开发语言·后端·golang
xiaoye-duck21 分钟前
《C++ 项目:手写大模型接入 SDK》DeepSeek接口设计(上):初始化和全量响应
开发语言·网络·c++·语言模型
fpcc26 分钟前
跟我学c++中级篇—streambuf在glog中的应用分析
开发语言·c++
吾与谁归in28 分钟前
C#脚本工具,支持智能提示自动补全(WPF + Roslyn + AvalonEdit)
开发语言·c#·wpf
码匠许师傅38 分钟前
【C++三方组件】OpenCV:计算机视觉的事实标准
c++
昂好的day39 分钟前
WPF 从零入门(上):WPF 是什么、XAML 语法、布局与样式全解
开发语言·c#·wpf
飞浪纪元[FWC–FE]42 分钟前
Qt 构建排错实录:从 MSB 1009 到生成器选型
开发语言·数据库·qt
fly-iot43 分钟前
【FDE知识库】(1):AI 编程时代开发语言选型分析,云使用go,企业用java,算法python,底层c
开发语言·人工智能·golang
云边有个稻草人44 分钟前
Prometheus Pushgateway教程:离线任务指标上报、Shell与Python示例、远程访问
开发语言·python·prometheus