c++中set和unordered_set的区别
作用
set与unordered_set一样,都是关联式容器,和 map 容器不同,使用 set 容器存储的各个键值对,要求键 key 和值 value 必须相等。
当使用 set 容器存储键值对时,只需要为其提供各键值对中的 value 值(也就是 key 的值)即可。
使用 set 容器存储的各个元素的值必须各不相同。
从语法上讲 set 容器并没有强制对存储元素的类型做 const 修饰,即 set 容器中存储的元素的值是可以修改的。但是,C++ 标准为了防止用户修改容器中元素的值,对所有可能会实现此操作的行为做了限制,使得在正常情况下,用户是无法做到修改 set 容器中元素的值的。
下面主要讨论一下set和unordered_set的区别之处。
set
- 定义于<set>头文件
- 底层实现通常是平衡二叉树
- 元素自动排序,这为查找元素提供了良好性能,但同时也造成了一个重要限制:不能直接改变元素值,因为这会打乱原本正确的顺序
- 查找函数具有对数复杂度
- 要改变元素的值,必须先删除该元素,再插入新元素
使用示例如下:
// Program to print elements of set
#include <set>
using namespace std;
int main()
{
set<int> s;
s.insert(5);
s.insert(1);
s.insert(6);
s.insert(3);
s.insert(7);
s.insert(2);
cout << "Elements of set in sorted order: \n";
for (const auto& it : s)
cout << it << " ";
return 0;
}
输出结果:
Elements of set in sorted order:
1 2 3 5 6 7
unordered_set
- 定义于<unordered_set>头文件
- 底层实现通常是hash-table
- 元素是无序的
- 插入、删除、查找元素的时间复杂度是常量的(排除偶尔的rehashing导致的线性复杂度)
示例:
// Program to print elements of set
#include <unordered_set>
using namespace std;
int main()
{
unordered_set<int> s;
s.insert(5);
s.insert(1);
s.insert(6);
s.insert(3);
s.insert(7);
s.insert(2);
cout << "Elements of unordered_set: \n";
for (auto it : s)
cout << it << " ";
return 0;
}
输出结果:
Elements of set in sorted order:
2 7 5 1 6 3
小结
综上,以下情况使用set:
- 需要排序的数据
- 需要通过前序、后序等方式遍历元素或者查找前继后继元素
- 想要使用binary_search(), lower_bound() and upper_bound()等需要在有序元素上使用的方法
- 其他平衡二叉树具有而hash表没有的优点
在以下情况使用unordered_set:
- 仅需要保存互异的元素而不需要排序
- 只需要获取单个元素而不需要遍历
它们的区别概览:

最后通过set进行按序查找前继后继元素的示例如下:
// Program to print inorder predecessor and inorder successor
#include <set>
using namespace std;
set<int> s;
void inorderPredecessor(int key)
{
if (s.find(key) == s.end()) {
cout << "Key doesn't exist\n";
return;
}
set<int>::iterator it;
it = s.find(key); // get iterator of key
// If iterator is at first position
// Then, it doesn't have predecessor
if (it == s.begin()) {
cout << "No predecessor\n";
return;
}
--it; // get previous element
cout << "predecessor of " << key << " is=";
cout << *(it) << "\n";
}
void inorderSuccessor(int key)
{
if (s.find(key) == s.end()) {
cout << "Key doesn't exist\n";
return;
}
set<int>::iterator it;
it = s.find(key); // get iterator of key
++it; // get next element
// Iterator points to NULL (Element does
// not exist)
if (it == s.end())
{
cout << "No successor\n";
return;
}
cout << "successor of " << key << " is=";
cout << *(it) << "\n";
}
int main()
{
s.insert(1);
s.insert(5);
s.insert(2);
s.insert(9);
s.insert(8);
inorderPredecessor(5);
inorderPredecessor(1);
inorderPredecessor(8);
inorderSuccessor(5);
inorderSuccessor(2);
inorderSuccessor(9);
return 0;
}
输出如下:
predecessor of 5 is=2
No predecessor
predecessor of 8 is=5
successor of 5 is=8
successor of 2 is=5
No successor
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)