C++ 中 std::set 用法详解(2025-2026 最新标准视角)

std::set 是 C++ STL(标准模板库)中最常用的有序关联容器之一,属于集合(set)的实现

一、set 的核心特点(必须先记住)

特性 说明 实际意义
元素唯一 自动去重,插入重复元素会被忽略 天然适合“集合”概念
自动排序 默认按升序排列(使用 < 比较) 插入后无需手动排序
底层实现 红黑树(平衡二叉搜索树) 插入/删除/查找均为 O(log n)
不可修改元素 只能插入/删除,不能直接修改已有元素的值(因为会破坏排序) 想改值 → 删除旧的 + 插入新的
有序遍历 迭代器天然有序(从 begin() 到 end() 就是升序) 适合需要有序输出的场景
不支持随机访问 没有 [] 运算符,只能通过迭代器或 find() 访问 不能像 vector 那样用下标

一句话总结:
set = 自动排序 + 自动去重 + 高效查找/插入/删除 的有序集合

二、头文件与基本声明

#include <set>
#include <iostream>

using namespace std;

最常用声明方式:

set<int> s;                     // 默认升序
set<int, greater<int>> s2;      // 降序
set<string> names;              // 存储字符串(按字典序)

三、常用成员函数一览(高频使用)

函数 功能 时间复杂度 示例用法 返回值说明
insert(x) 插入元素 x O(log n) s.insert(5); pair<iterator, bool>(插入成功返回 true)
erase(x) / erase(it) 删除元素 x 或迭代器位置的元素 O(log n) s.erase(5); s.erase(s.find(3)); 删除的元素个数(通常 0 或 1)
find(x) 查找元素 x O(log n) auto it = s.find(10); 找到返回迭代器,没找到返回 end()
count(x) 元素 x 出现的次数(只有 0 或 1) O(log n) if (s.count(7)) cout << “存在”; 0 或 1
size() / empty() 大小 / 是否为空 O(1) if (s.empty()) …
clear() 清空所有元素 O(n) s.clear();
begin() / end() 指向第一个 / 最后一个后一个元素 O(1) for(auto it = s.begin(); it != s.end(); ++it) 有序迭代器
lower_bound(x) 第一个 ≥ x 的元素 O(log n) auto it = s.lower_bound(10); ≥ x 的第一个位置
upper_bound(x) 第一个 > x 的元素 O(log n) auto it = s.upper_bound(10); > x 的第一个位置
equal_range(x) [lower_bound(x), upper_bound(x)) O(log n) auto [l, r] = s.equal_range(5); 等于 x 的范围(set 中最多一个)

四、代码实战示例(最常用场景)

1. 基本插入 + 遍历
set<int> s;
s.insert(30);
s.insert(10);
s.insert(50);
s.insert(20);
s.insert(10);  // 重复元素,自动忽略

// 遍历(天然有序)
for (int x : s) {
    cout << x << " ";           // 输出:10 20 30 50
}
cout << endl;
2. 查找与删除
if (s.find(20) != s.end()) {
    cout << "找到 20\n";
    s.erase(20);                // 删除 20
}

if (s.count(100) == 0) {
    cout << "100 不存在\n";
}
3. lower_bound / upper_bound 经典用法(区间查询)
// 找出 [20, 40] 之间的所有元素
auto l = s.lower_bound(20);
auto r = s.upper_bound(40);

for (auto it = l; it != r; ++it) {
    cout << *it << " ";         // 输出 20 30
}
4. 自定义比较器(降序 / 自定义结构体)
// 降序 set
set<int, greater<int>> desc;
desc.insert({5, 1, 8, 3});     // 输出顺序:8 5 3 1

// 自定义结构体按某个字段排序
struct Student {
    string name;
    int score;
};

auto cmp = [](const Student& a, const Student& b) {
    return a.score > b.score;   // 按分数降序
};

set<Student, decltype(cmp)> students(cmp);
5. pair / 多关键字排序(非常常见)
set<pair<int, string>> s;          // 默认先按 int 升序,再按 string 升序
s.insert({90, "Alice"});
s.insert({85, "Bob"});
s.insert({90, "Charlie"});         // 90 的 Alice 在 Charlie 前面(string 字典序)

五、set vs unordered_set vs multiset

容器 是否有序 是否允许重复 查找/插入复杂度 适用场景
set O(log n) 需要有序 + 去重
unordered_set 平均 O(1) 只需快速去重 + 不关心顺序
multiset O(log n) 需要有序 + 允许重复元素

六、2026 年使用建议(面试 + 生产)

  1. 需要有序输出范围查询 → 首选 set
  2. 只关心去重不关心顺序 → 用 unordered_set(更快)
  3. 需要有序 + 允许重复 → 用 multiset
  4. 元素是自定义类型 → 必须提供 < 运算符 或 自定义比较器
  5. 频繁增删 + 元素量大(>10^6) → 优先考虑 unordered_set(但注意哈希冲突最坏 O(n))
  6. 面试高频题:用 set 实现滑动窗口中位数、最近请求次数等

七、最简总结(背下来就够面试)

  • set:有序、唯一、红黑树、O(log n) 操作
  • 插入重复元素自动忽略
  • 不可通过迭代器修改元素值(const)
  • lower_bound / upper_bound 是最强范围查询利器

如果你现在有具体的使用场景(比如怎么用 set 去重排序、怎么实现有序去重输出、自定义比较器报错等),可以贴代码或描述需求,我帮你写最优解法。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐