set容器基于红黑树实现,自动排序且元素唯一,插入、删除、查找时间复杂度均为O(log n);支持自定义排序规则,适用于需有序、稳定性能的场景。

C++ STL中的set容器是处理需要去重和有序数据场景的利器。它基于红黑树实现,提供了高效的插入、删除和查找能力,是算法设计和日常开发中不可或缺的工具。
核心特性与底层原理
set容器最显著的特点是自动排序和元素唯一。当你向set中插入元素时,它会根据预设的规则(默认升序)将元素放置在正确位置,并且如果该元素已存在,则插入操作会被忽略,从而实现自动去重。
这一切高效运作的背后是其底层的红黑树数据结构。红黑树是一种自平衡二叉搜索树,它通过特定的规则(如节点颜色、旋转等)来保证树的高度始终接近log(n)。这使得set的所有核心操作——插入、删除、查找——的平均时间复杂度都稳定在O(log n),性能远超需要遍历的线性容器。
需要注意的是,由于排序依赖于元素的值,直接修改set中元素的值会破坏树的结构。正确的做法是先用erase()删除旧元素,再用insert()插入新元素。
立即学习“C++免费学习笔记(深入)”;
常用操作与成员函数
使用set前需包含头文件
- 插入元素: 使用insert(val)。若val已存在,插入失败,函数返回一个pair,其中second为false,可用于判断是否为新元素。
- 删除元素: 使用erase(val)删除指定值,或erase(iterator)删除迭代器指向的元素。
- 查找元素: 使用find(val),若找到则返回指向该元素的迭代器,否则返回end()。也可用count(val)检查元素是否存在(对于set,结果只能是0或1)。
- 范围查询: lower_bound(key)返回第一个不小于key的元素的迭代器;upper_bound(key)返回第一个大于key的元素的迭代器。这对区间操作非常有用。
- 遍历容器: 通过begin()和end()获取双向迭代器,进行正向或反向遍历。遍历的结果是有序的。
性能对比与选型建议
理解不同容器的性能特点是写出高效代码的关键。
与同样能去重的unordered_set相比,set的主要区别在于底层结构:unordered_set基于哈希表,平均操作复杂度为O(1),速度更快,但元素无序,且在哈希冲突严重时性能可能退化到O(n)。而set虽然平均O(log n)稍慢,但性能稳定,且能保持元素有序,支持范围查询。
与序列式容器如vector相比,若vector未排序,查找复杂度为O(n);即使排序后使用二分查找,也能达到O(log n),但频繁的插入删除代价高昂(O(n))。因此,当你的场景涉及频繁的动态增删查时,set是更好的选择。
简单来说,如果只关心“有没有”且追求极致速度,选unordered_set;如果需要“有序”或“稳定”的性能,set是可靠的选择。
高级应用:自定义排序规则
set默认按升序排列,但你可以通过提供自定义的比较函数来改变这一行为。这在处理复杂对象时尤其有用。
方法是创建一个仿函数(函数对象),并将其作为模板参数传入。例如,要让set按整数的绝对值大小排序:
struct AbsCompare {bool operator()(int a, int b) const {
return abs(a) }
};
std::set
mySet.insert(-5);
mySet.insert(3);
mySet.insert(-2);
// 集合内元素逻辑顺序为:-2, 3, -5 (按绝对值从小到大)
这样,你就可以灵活地控制set中元素的组织方式,以满足特定的业务需求。
基本上就这些,掌握好set能让代码更优雅高效。









