
🔥个人主页:Cx330🌸
❄️个人专栏:《C语言》《LeetCode刷题集》《数据结构-初阶》《C++知识分享》
《优选算法指南-必刷经典100题》《Linux操作系统》:从入门到入魔
🌟心向往之行必能至
🎥Cx330🌸的简介:

目录
[一、set 核心特性与适用场景](#一、set 核心特性与适用场景)
[1. 核心特性](#1. 核心特性)
[2. 适用场景](#2. 适用场景)
[二、set 基础用法(含代码示例)](#二、set 基础用法(含代码示例))
[2.1 头文件与命名空间](#2.1 头文件与命名空间)
[2.2 初始化与插入:](#2.2 初始化与插入:)
[2.3 查找与删除:](#2.3 查找与删除:)
[2.4 lower_bound 与 upper_bound:"定位神器"](#2.4 lower_bound 与 upper_bound:“定位神器”)
[三、multiset:支持重复 key](#三、multiset:支持重复 key)
[3.1 set与multiset差异:](#3.1 set与multiset差异:)
[4.1:环形链表 II](#4.1:环形链表 II)
[4.2 两个数组的交集](#4.2 两个数组的交集)
前言:
在 C++ STL(标准模板库)中,set 是一个极具实用价值的关联容器,核心特性是自动排序和元素唯一。它底层基于红黑树(平衡二叉搜索树)实现,保证了插入、查找、删除等操作的高效性(时间复杂度均为 O (logn))。无论是数据去重、有序遍历,还是区间查询,
set都能轻松应对。本文将从基础概念到进阶实战,带你全面掌握set的用法

一、set 核心特性与适用场景
参考文档:set - C++ Reference
1. 核心特性
- 有序性 :元素会按照默认规则(升序)自动排序,也支持自定义排序规则;
- 唯一性 :不允许重复元素,插入重复值会被自动忽略;
- 底层实现 :红黑树(平衡二叉搜索树),兼顾查询和插入效率;
- 元素不可直接修改:set 的元素是 const 类型(修改会破坏排序结构),若需修改需先删除旧元素再插入新元素。
2. 适用场景
- 数据去重并保持有序(如考试成绩排名、日志时间排序);
- 高效查找元素是否存在(如用户 ID 校验);
- 区间查询(如查找 [10, 50] 之间的数值);
- 有序遍历需求(无需手动排序)。
set的声明:

二、set 基础用法(含代码示例)
2.1 头文件与命名空间
使用set 需包含头文件 <set>,并使用 std 命名空间:
cpp
#include <set>
#include <iostream>
using namespace std; // 简化代码,实际项目可根据需求选择是否使用
set相关接口:

2.2 初始化与插入:
set 支持多种插入方式,插入后自动去重并按升序排列 代码示例**(注意看注释)**:
- 插入单个元素:insert(val),返回
pair<iterator, bool>(迭代器指向插入位置,bool 表示是否插入成功); - 插入多个元素:insert(initializer_list)(C++11+);
- 插入范围元素:insert(begin, end)
cpp
#include<iostream>
#include<set>
using namespace std;
void test_set1()
{
set<int> s;
s.insert(3);
s.insert(1);
s.insert(2);
s.insert(5);
s.insert(3);
s.insert(5);
s.insert(6);
//遍历结果:去重+有序
set<int>::iterator it = s.begin();
while (it != s.end())
{
//*it = 1;//不能修改
cout << *it << " ";
++it;
}
cout << endl;
for (auto& e : s)
{
cout << e << " ";
}
cout << endl;
for (auto& e : s)
{
cout << e << " ";
}
cout << endl;
}
说明:元素会按照默认规则(升序)自动排序,也支持自定义排序规则;
2.3 查找与删除:
- 根据键删除:erase(val),返回删除的元素个数(0 或 1,因为 set 无重复);
- 根据迭代器删除:erase(iter),无返回值(需确保迭代器有效);
- 范围删除:erase(begin, end),无返回值。
cpp
void test_set1()
{
set<int> s;
s.insert(3);
s.insert(1);
s.insert(2);
s.insert(5);
s.insert(3);
s.insert(5);
s.insert(6);
//遍历结果:去重+有序
set<int>::iterator it = s.begin();
while (it != s.end())
{
//*it = 1;//不能修改
cout << *it << " ";
++it;
}
cout << endl;
for (auto& e : s)
{
cout << e << " ";
}
cout << endl;
int x = 0;
cin >> x;
s.erase(x);//在set里就删,不在就不删
cout << s.erase(x) << endl;
auto pos = s.find(x);
if (pos != s.end())
{
s.erase(pos);
}
//单独判断在不在
//if (s.count(x))
//{
//}
for (auto& e : s)
{
cout << e << " ";
}
cout << endl;
}

2.4 lower_bound 与 upper_bound:"定位神器"
set 的区间操作依赖lower_bound和upper_bound,用于快速定位边界,结合erase可高效删除区间元素:
思考:如果我们要删除【3,8】区间的元素呢?
答:find()函数查找3和8,依次删除
那如果没有3这个元素呢?我们接着往下看:

cpp
void test_set2()
{
set<int> s;
s.insert(3);
s.insert(1);
s.insert(2);
s.insert(5);
s.insert(3);
s.insert(5);
s.insert(6);
s.insert(7);
s.insert(9);
for (auto& e : s)
{
cout << e << " ";
}
cout << endl;
//[3,8]之间的值都删掉
//auto it = s.find(3);//如果没有3呢?
//查找 >=3 的值(有3就返回3,没有3就返回比3大一点的值)
auto it1 = s.lower_bound(3);
//查找 <=8 的值(有8就返回8,没有8就返回比8小一点的值)
auto it2 = s.upper_bound(8);
s.erase(it1, it2);
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
}

说明:
- lower_bound 与upper_bound 的查找时间复杂度:O(logN),是一种较为优秀的
三、multiset:支持重复 key
multiset 与 set 接口一致,核心差异是允许重复 key,适用于需要存储相同元素并统计频率的场景:
cpp
void test_set3()
{
multiset<int> s;
s.insert(3);
s.insert(1);
s.insert(2);
s.insert(5);
s.insert(3);
s.insert(5);
s.insert(6);
s.insert(3);
//遍历结果:有序(不去重)
multiset<int>::iterator it = s.begin();
while (it != s.end())
{
//*it = 1;//不能修改
cout << *it << " ";
++it;
}
cout << endl;
// multiset里查中序第一个3
auto pos = s.find(3);
//while (pos != s.end())
while (pos != s.end() && *pos == 3)//只打印3
{
cout << *pos << " ";
++pos;
}
cout << endl;
//s.erase(3);
// [ )
typedef multiset<int>::iterator IT;
//std::pair<multiset<int>::iterator, multiset<int>::iterator> ret = s.equal_range(3);
//std::pair<IT, IT> ret = s.equal_range(3);
auto ret = s.equal_range(3);
cout << s.count(3) << endl;
cout << s.erase(3) << endl;
for (auto& e : s)
{
cout << e << " ";
}
cout << endl;
}
int main()
{
//test_set1();
//test_set2();
test_set3();
return 0;
}

3.1 set与multiset差异:

四、set实战技巧
4.1:环形链表 II
题目链接 :142. 环形链表 II - 力扣(LeetCode)

说明:之前我们是利用快慢指针来解题,我们再来回顾一下快慢指针的做法:
图解:

C算法代码(快慢指针):
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode *detectCycle(struct ListNode *head)
{
ListNode*slow=head;
ListNode*fast=head;
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;
if(slow==fast)
{
//找相遇点
ListNode*pcur=head;
while(pcur!=slow)
{
pcur=pcur->next;
slow=slow->next;
}
return pcur;
}
}
return NULL;
}
C++算法代码:
cpp
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head)
{
set<ListNode*> s;
ListNode* cur=head;
while(cur)
{
auto it=s.find(cur);
if(it==s.end())
{
s.insert(cur);
}
else
{
return *it;
}
cur=cur->next;
}
return nullptr;
}
};
4.2 两个数组的交集
题目链接 :349. 两个数组的交集 - 力扣(LeetCode)

图解:

C++算法代码:
cpp
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2)
{
//去重+排序
set<int> s1(nums1.begin(),nums1.end());
set<int> s2(nums2.begin(),nums2.end());
vector<int> v;
auto it1=s1.begin();
auto it2=s2.begin();
while(it1!=s1.end()&&it2!=s2.end())
{
if(*it1<*it2)
{
it1++;
}
else if(*it1>*it2)
{
it2++;
}
else
{
v.push_back(*it1);
++it1;
++it2;
}
}
return v;
}
};
结尾:
结语:掌握 set 的用法,能大幅简化 "去重 + 排序" 类场景的开发,提升代码效率和可读性。建议结合实际场景多练习,比如日志去重排序、数据区间统计等,加深对 set 特性的理解
