#include<bits/stdc++.h>
using namespace std;
int main(){
set<int>s;
// write your code here......
int a;
while(cin>>a){
s.insert(a);
}
for(auto x : s){
cout<<x<<" ";
}
return 0;
}
C++ set 容器 超详细使用教程(零基础易懂,和vector对比学+最全用法+避坑)
你已经熟练掌握了vector,学set会非常轻松!因为二者都是STL容器,语法相通度极高,我会全程和vector对比讲解,帮你快速吃透;set是C++中第二常用的容器,核心特性是自动去重 + 自动升序排序,刚好弥补了vector「可重复、无序」的缺点,两者互补使用能解决95%的存储需求。
✅ 一、先搞懂:set 是什么?和 vector 的核心区别(重中之重,必记)
1. set 核心定义
set 是STL中的集合容器,底层是红黑树实现,本质是一个**【无重复元素】+【自动升序排序】** 的存储容器。✅ 两个核心特性(set的灵魂,记住这2点就懂了一半):
① 自动去重:向set中插入重复元素时,set会自动忽略重复的元素,容器中永远不会有重复值;② 自动升序:向set中插入元素后,set会自动将所有元素按「从小到大」排序,不用手动调用sort。
2. set VS vector 核心区别(对比学,零记忆负担,你最需要的)
这是你最关心的点,二者用法相通,但特性互补,表格对比一目了然,记死!
元素重复性 | 允许重复元素 | ✅ 自动去重,无重复元素 |
排序性 | 无序,插入顺序=存储顺序 | ✅ 自动升序排序(从小到大) |
下标访问 | 支持
随机访问 | ❌ 不支持下标访问!(无[]) |
插入方式 |
尾部插入 |
任意位置插入(自动排序) |
查找效率 | 低(遍历查找,数据越多越慢) | 极高(树结构查找,速度不受数据量影响) |
适用场景 | 需重复存储、需下标访问、无序场景 | 需去重、需排序、无重复元素的场景 |
✅ 一句话总结什么时候用set:当你的需求是「存数据 + 去重 + 排序」时,无脑用set,一行代码不用写,自动完成这三个功能!
✅ 二、set 前置必备(和vector完全一样,直接复用)
1. 必加头文件
所有set代码,必须引入这个头文件,缺一不可:
#include <set> // set专属头文件 #include <iostream> using namespace std;
2. 命名空间
和vector一样,加using namespace std; 后可以直接写set,不加则写std::set,推荐加上,简洁。
✅ 三、核心语法1:set的「定义与初始化」(4种常用写法,和vector几乎一致)
语法格式:set<存储的数据类型> 容器名;
和vector完全一样!
<>里写存储的类型:int/char/string等,set可以存的类型和vector完全相同。✨ 注意:set初始化时,即使写了重复元素,也会自动去重;即使乱序写入,也会自动排序!
#include <iostream>
#include <set>
using namespace std;
int main() {
// 写法1:定义空的set,最常用!先创建空集合,后续插入元素
set<int> s1;
// 写法2:定义时直接初始化赋值(推荐,初始化已知数据)
set<int> s2 = {5, 2, 8, 2, 5, 1};
// ✨ 特性体现:自动去重+升序 → s2实际存储:{1,2,5,8}
// 写法3:用一个set初始化另一个set(拷贝初始化)
set<int> s3(s2); // s3 = {1,2,5,8}
// 写法4:存储字符串(同理,存其他类型只改<>里的内容)
set<string> s4 = {"c++", "stl", "set", "c++"};
// 自动去重+按字符串字典序排序 → {"c++","set","stl"}
return 0;
}
✅ 四、核心语法2:set 最常用的「增删改查」操作(8个必学,和vector对比,易记)
这是set的核心用法,约80%的场景只用到这8个操作,所有操作都是set对象名.函数名(),和vector的调用方式完全一致!
所有例子都基于这个基础:
set<int> s = {5,2,8,1};→ 实际存储{1,2,5,8}✨ 核心提醒:set没有下标访问s[i],这个是和vector最大的区别,一定不要写!
✅ 【增】插入元素 【最常用TOP1】 insert(值)
- 功能:向set中插入一个元素,自动去重+自动排序,插入后容器内元素始终有序无重复;
- 和vector的
push_back()对应,但比它强大太多;
s.insert(3); // s变成 {1,2,3,5,8}
s.insert(5); // 插入重复元素,set自动忽略,容器无变化
✅ 【查】获取容器长度 【最常用TOP2】 size()
- 功能:返回set中有效元素的个数;
- 和vector的
size()完全一样,无区别;
cout << s.size() << endl; // 初始s有4个元素 → 输出4 s.insert(3); cout << s.size() << endl; // 插入后5个元素 → 输出5
✅ 【查】判断元素是否存在 【最常用TOP3】 count(值)
- 功能:判断set中是否包含指定值,返回值只有两种: 返回 1 → 存在该元素;返回 0 → 不存在该元素;
- 这个是set的「王牌功能」,比vector的查找高效100倍,判断元素是否存在必用这个;
cout << s.count(5) << endl; // 存在5 → 输出1 cout << s.count(9) << endl; // 不存在9 → 输出0
✅ 【查】查找元素迭代器 【常用】 find(值)
- 功能:查找指定值,返回一个迭代器: 如果找到元素 → 迭代器指向该元素;如果没找到 → 迭代器等于 s.end();
- 用法和STL的
find()算法类似,但效率远高于vector的find,set的find是专门为集合优化的;
set<int>::iterator it = s.find(5);
if(it != s.end()){
cout << "找到元素:" << *it << endl; // 输出 5
}
✅ 【判空】判断是否为空 empty()
- 功能:判断set里有没有元素,为空返回
true,不为空返回false; - 和vector的
empty()完全一样,无区别;
set<int> s_empty;
if(s_empty.empty()){
cout << "集合为空" << endl; // 执行这句
}
✅ 【删】清空所有元素 clear()
- 功能:清空set里的所有元素,清空后
size()变为0; - 和vector的
clear()完全一样,无区别;
s.clear(); cout << s.size() << endl; // 输出0
✅ 【删】删除指定元素 【常用】 erase(值)
- 功能:删除set中指定值的元素,如果元素不存在,无任何操作;
- 精准删除,不用关心元素位置,非常好用;
s.erase(5); // 删除元素5 → s变成 {1,2,8}
s.erase(9); // 删除不存在的元素,无变化
✅ 【删】删除迭代器指向的元素 erase(迭代器)
- 功能:删除迭代器指向的元素,配合
find()使用,精准删除找到的元素;
set<int>::iterator it = s.find(2);
if(it != s.end()){
s.erase(it); // 删除元素2 → s变成 {1,5,8}
}
✅ 五、核心语法3:set 的「遍历方式」(2种必学,无下标!和vector互通)
因为set不支持下标访问 s[i],所以它的遍历方式只有2种,全部和vector的遍历方式互通,你已经完全掌握了,零成本上手!
所有遍历例子都基于:
set<int> s = {5,2,8,1};→ 实际存储{1,2,5,8}
✅ 方式1:迭代器遍历 【最推荐、最常用】 set<int>::iterator
- 核心:set的迭代器和vector的迭代器语法完全一模一样!你可以直接复用vector的迭代器知识;
- 规则回顾:迭代器=容器专属指针,
*it取值、it++移动、s.begin()首元素、s.end()尾后标志;
set<int>::iterator it;
// 遍历输出所有元素,自动是升序的
for (it = s.begin(); it != s.end(); it++) {
cout << *it << " ";
}
// 输出:1 2 5 8
✅ 优势:STL标准写法,效率最高,99%的场景用这个就够了,必学必掌握!
✅ 方式2:范围for遍历 【C++11极简写法】 for(auto x : s)
- 语法和vector的范围for完全一样,一行代码搞定遍历,不用写迭代器,超级简洁;
auto会自动推导类型为int,不用手动写set<int>::iterator;
// 范围for遍历,自动升序输出
for (auto x : s) {
cout << x << " ";
}
// 输出:1 2 5 8
✅ 优势:代码量最少,适合快速写代码,日常开发首选!
✅ 六、set 的「经典实战案例」(2个高频场景,你一定会用到!)
结合你之前写的vector输入案例,我给你2个set的经典实战代码,都是笔试/面试/开发的高频场景,直接复制就能运行,帮你快速上手!
✅ 案例1:输入若干整数 → 自动去重+升序输出(最经典!替代vector+sort+去重)
这个案例完美体现set的核心价值:如果用vector实现这个需求,需要「存数据→sort排序→手动去重」,至少5行代码;用set实现,只需要插入元素+遍历输出,自动完成所有需求!
#include <iostream>
#include <set>
using namespace std;
int main() {
set<int> s;
int a;
// 循环输入整数,插入set自动去重+排序
while(cin >> a){
s.insert(a);
}
// 遍历输出,自动升序无重复
for(auto x : s){
cout << x << " ";
}
return 0;
}
✅ 测试效果:输入 5 2 8 2 5 1 9 1 → 输出 1 2 5 8 9,完美去重+排序!✅ 结束输入:和vector一样,Windows按Ctrl+Z回车,Linux/Mac按Ctrl+D。
✅ 案例2:判断某个元素是否存在(set的王牌场景)
比如:输入一个目标数,判断它是否在输入的数列中,用set的count()实现,一行判断,效率极高!
#include <iostream>
#include <set>
using namespace std;
int main() {
set<int> s = {1,3,5,7,9};
int target = 5;
// 一行判断元素是否存在
if(s.count(target)){
cout << target << " 存在于集合中" << endl;
}else{
cout << target << " 不存在于集合中" << endl;
}
return 0;
}
✅ 七、set 的「易错点避坑指南」(新手必看,3个经典坑,全部避开!)
这是set最容易踩坑的3个点,都是新手高频错误,记死就能避开,非常重要!
❌ 坑1:试图用下标访问set元素 s[i] → 编译报错!
set<int> s = {1,2,3};
cout << s[0] << endl; // ❌ 编译报错!set没有下标运算符[]
✅ 原因:set是无序的树结构,没有下标概念,只能通过迭代器/范围for遍历访问元素。
❌ 坑2:试图修改set中的元素 → 编译报错!
set<int>::iterator it = s.find(2); *it = 20; // ❌ 编译报错!set的元素是「只读的」
✅ 原因:set的元素是const常量,不允许修改!因为修改元素会破坏set的排序规则。如果想改元素,只能「先删除旧值,再插入新值」:
// 正确修改:删旧值+插新值 s.erase(2); s.insert(20);
❌ 坑3:认为set的insert插入顺序就是存储顺序 → 错误!
set<int> s; s.insert(5); s.insert(2); s.insert(8); // 遍历输出不是 5 2 8,而是 1 2 5 → set自动排序
✅ 原因:set的核心特性就是自动排序,无论插入顺序如何,存储的元素永远是升序的。
✅ 八、set 常用操作速查表(收藏备查,不用死记,看表就行)
set<int> s; // 定义空set s.insert(x); // 插入元素x(自动去重+排序) s.size(); // 获取元素个数 s.count(x); // 判断元素x是否存在(1=存在,0=不存在) s.find(x); // 查找x,返回迭代器 s.empty(); // 判断是否为空 s.clear(); // 清空所有元素 s.erase(x); // 删除指定值x的元素 s.erase(it); // 删除迭代器it指向的元素
✅ 九、vector 和 set 如何选择?(终极选择原则,无脑套用)
很多时候你会纠结用哪个容器,这里给你一个万能选择原则,满足任意一个条件就用对应容器,永远不会选错!
✅ 用 vector 的情况(满足其一即可)
- 需要存储重复元素;
- 需要下标访问元素(比如随机取值、按索引修改);
- 对元素顺序有要求(插入顺序=存储顺序);
✅ 用 set 的情况(满足其一即可)
- 需要自动去重;
- 需要自动排序;
- 需要频繁判断元素是否存在(查找效率极高);
✅ 总结(核心知识点,一句话记死,永不忘记)
- set 是集合容器,核心特性:自动去重 + 自动升序排序,完美弥补vector的缺点;
- set的语法和vector高度互通,定义、遍历、常用函数几乎一样,上手零成本;
- set的核心操作:
insert()插入、count()判存在、erase()删除、迭代器遍历; - set的3个坑:无下标、元素只读、自动排序,记死就能避开;
- 选择原则:要去重排序用set,要重复下标用vector,二者互补,是C++最常用的两个容器!
你已经熟练掌握了vector,学set就是水到渠成的事,二者结合使用,能解决你遇到的绝大多数存储需求,恭喜你又掌握了一个超实用的STL容器!🎉

京公网安备 11010502036488号