#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 核心区别(对比学,零记忆负担,你最需要的)

这是你最关心的点,二者用法相通,但特性互补,表格对比一目了然,记死!

元素重复性

允许重复元素

✅ 自动去重,无重复元素

排序性

无序,插入顺序=存储顺序

✅ 自动升序排序(从小到大)

下标访问

支持

v[i]

随机访问

❌ 不支持下标访问!(无[])

插入方式

push_back()

尾部插入

insert()

任意位置插入(自动排序)

查找效率

低(遍历查找,数据越多越慢)

极高(树结构查找,速度不受数据量影响)

适用场景

需重复存储、需下标访问、无序场景

需去重、需排序、无重复元素的场景

一句话总结什么时候用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 的情况(满足其一即可)

  1. 需要存储重复元素
  2. 需要下标访问元素(比如随机取值、按索引修改);
  3. 对元素顺序有要求(插入顺序=存储顺序);

✅ 用 set 的情况(满足其一即可)

  1. 需要自动去重
  2. 需要自动排序
  3. 需要频繁判断元素是否存在(查找效率极高);

✅ 总结(核心知识点,一句话记死,永不忘记)

  1. set 是集合容器,核心特性:自动去重 + 自动升序排序,完美弥补vector的缺点;
  2. set的语法和vector高度互通,定义、遍历、常用函数几乎一样,上手零成本;
  3. set的核心操作:insert()插入、count()判存在、erase()删除、迭代器遍历;
  4. set的3个坑:无下标、元素只读、自动排序,记死就能避开;
  5. 选择原则:要去重排序用set,要重复下标用vector,二者互补,是C++最常用的两个容器!

你已经熟练掌握了vector,学set就是水到渠成的事,二者结合使用,能解决你遇到的绝大多数存储需求,恭喜你又掌握了一个超实用的STL容器!🎉