C++中set用法详解

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)插入元素 xO(log n)s.insert(5);pair(插入成功返回 true)
erase(x) / erase(it)删除元素 x 或迭代器位置的元素O(log n)s.erase(5); s.erase(s.find(3));删除的元素个数(通常 0 或 1)
find(x)查找元素 xO(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

容器是否有序是否允许重复查找/插入复杂度适用场景
setO(log n)需要有序 + 去重
unordered_set平均 O(1)只需快速去重 + 不关心顺序
multisetO(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 去重排序、怎么实现有序去重输出、自定义比较器报错等),可以贴代码或描述需求,我帮你写最优解法。

文章已创建 4845

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部