set
set
set 是一个基于平衡二叉搜索树(如红黑树)的容器。它会自动对元素进行排序,且保证每个元素在集合中唯一。因为元素是有序的,所以查找、插入和删除的时间复杂度都是 O(log N)。
主要特性:
- 唯一性:
set中的元素不允许重复。 - 自动排序:元素会自动按照升序排序,如果需要自定义排序规则,可以通过自定义比较器(
comp)来实现。 - 查找操作:
set提供了 O(log N) 时间复杂度的查找操作。 - 支持迭代器:
set支持通过迭代器遍历其元素。 - 不支持修改元素:
set中的元素一旦插入后,不能修改;但是可以删除和插入新的元素。
底层实现原理
set 底层通常使用 红黑树(Red-Black Tree)来实现。红黑树是一种自平衡的二叉查找树,它通过对树结构的平衡操作确保插入、删除和查找操作的时间复杂度为 O(log N)。
红黑树的基本性质:
- 每个节点要么是红色,要么是黑色。
- 根节点是黑色。
- 叶节点(NIL 节点)是黑色。
- 红色节点的子节点必须是黑色的(即没有两个红色节点相连)。
- 从任一节点到其所有后代叶子节点的路径上,黑色节点的数量相同。
由于红黑树的这些性质,set 可以确保在最坏情况下,操作时间复杂度不会超过 O(log N),从而保证效率。
常用接口
- 构造函数
set<T>:默认构造函数,创建一个空的set。set<T>(begin, end):使用另一个范围内的元素来初始化set。set<T>(compare):使用指定的比较函数来初始化。set<T>(begin, end, compare):使用指定的比较函数和范围内的元素初始化。
- 插入元素
insert(const T& value):插入一个元素,若元素已存在,则不插入。insert(iterator hint, const T& value):在指定位置插入元素,若元素已存在,则不插入。emplace(args...):就地构造一个元素并插入,避免了不必要的拷贝或移动。
- 查找元素
find(const T& value):查找指定元素,返回一个迭代器,若找不到则返回end()。count(const T& value):返回集合中指定元素的个数(set中最多只有一个元素)。
- 删除元素
erase(const T& value):删除指定元素。erase(iterator pos):删除指定位置的元素。erase(iterator first, iterator last):删除指定范围内的元素。clear():删除所有元素。
- 大小和容量
size():返回元素个数。empty():判断集合是否为空。max_size():返回集合能够容纳的最大元素数。
- 迭代器
begin():返回指向集合第一个元素的迭代器。end():返回指向集合最后一个元素后一个位置的迭代器。rbegin():返回指向集合最后一个元素的反向迭代器。rend():返回指向集合第一个元素之前的反向迭代器。
- 自定义排序
set<T, Compare>:通过Compare类型的比较器来控制元素的排序。
示例代码
基本用法示例
#include <iostream>
#include <set>
int main() {
std::set<int> s;
// 插入元素
s.insert(10);
s.insert(20);
s.insert(15);
// 查找元素
if (s.find(20) != s.end()) {
std::cout << "Found 20!" << std::endl;
}
// 遍历 set
for (const int& x : s) {
std::cout << x << " ";
}
std::cout << std::endl;
// 删除元素
s.erase(15);
// 查看大小
std::cout << "Size of set: " << s.size() << std::endl;
return 0;
}
使用自定义排序规则
#include <iostream>
#include <set>
struct CustomCompare {
bool operator()(int a, int b) const {
return a > b; // 降序排列
}
};
int main() {
std::set<int, CustomCompare> s;
s.insert(10);
s.insert(20);
s.insert(15);
for (const int& x : s) {
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
常见问题
set和unordered_set的区别set:基于红黑树,元素有序,查找、插入、删除的时间复杂度为 O(log N)。unordered_set:基于哈希表,元素无序,查找、插入、删除的时间复杂度为 O(1)(平均情况下)。
set和map的区别set:存储的是单一的元素值。map:存储的是键值对(key-value)。
- 如何删除
set中的所有元素?- 使用
clear()方法。
- 使用
set中元素是否可以重复?- 不可以,
set中的每个元素是唯一的。
- 不可以,
- 如何查找
set中的元素是否存在?- 使用
find()方法,若元素存在则返回该元素的迭代器,否则返回end()。
- 使用
- 如何实现自定义排序?
- 通过定义一个比较函数或函数对象作为模板参数传递给
set。
- 通过定义一个比较函数或函数对象作为模板参数传递给
其他相关知识
multiset:与set类似,但允许重复元素。底层依然使用平衡二叉树。- 性能优化:在需要频繁查找或删除的场景下,
set是比vector或list更高效的选择。
源码
- 元素按键值自动排序,且键值就是实值,不允许重复。
- 不能通过迭代器修改元素值,因为修改会破坏排序规则,
set<T>::iterator实际是底层红黑树的const_iterator。 - 插入或删除元素时,除被删除元素外,其他迭代器依然有效。
- 提供交集、并集、差集、对称差集等算法。
- 底层实现为 红黑树,set 的操作基本是转调用红黑树的接口。
// set 的模板定义
template <class Key, // 元素类型(键类型)
class Compare = less<Key>, // 比较规则(缺省递增排序)
class Alloc = alloc> // 空间配置器
class set {
public:
// --------------------
// 类型定义(typedefs)
// --------------------
typedef Key key_type; // 键类型
typedef Key value_type; // 元素类型(set 的元素键值即实值)
typedef Compare key_compare; // 键的比较函数
typedef Compare value_compare; // 元素的比较函数(和键比较相同)
private:
/*
identity 定义在 <stl_function.h>:
template<class T>
struct identity : public unary_function<T, T> {
const T& operator()(const T& x) const { return x; }
};
identity 用于在底层红黑树中将 value_type 映射为 key_type
因为 set 的键值和实值相同,所以直接返回元素本身即可。
*/
// 底层容器类型:红黑树
typedef rb_tree<key_type, value_type,
identity<value_type>, // 键值提取器
key_compare, // 比较规则
Alloc> rep_type;
rep_type t; // 用红黑树实现 set
public:
// --------------------
// 指针与引用类型
// --------------------
typedef typename rep_type::const_pointer pointer;
typedef typename rep_type::const_pointer const_pointer;
typedef typename rep_type::const_reference reference;
typedef typename rep_type::const_reference const_reference;
// 注意:iterator 定义为红黑树的 const_iterator
// 这意味着 set 迭代器不能修改元素值(因为修改会破坏排序规则)
typedef typename rep_type::const_iterator iterator;
typedef typename rep_type::const_iterator const_iterator;
typedef typename rep_type::const_reverse_iterator reverse_iterator;
typedef typename rep_type::const_reverse_iterator const_reverse_iterator;
typedef typename rep_type::size_type size_type;
typedef typename rep_type::difference_type difference_type;
// --------------------
// 构造函数
// --------------------
set() : t(Compare()) {}
explicit set(const Compare& comp) : t(comp) {}
template <class InputIterator>
set(InputIterator first, InputIterator last)
: t(Compare()) { t.insert_unique(first, last); }
template <class InputIterator>
set(InputIterator first, InputIterator last, const Compare& comp)
: t(comp) { t.insert_unique(first, last); }
set(const set<Key, Compare, Alloc>& x) : t(x.t) {}
set<Key, Compare, Alloc>& operator=(const set<Key, Compare, Alloc>& x) {
t = x.t;
return *this;
}
// --------------------
// 访问器
// --------------------
key_compare key_comp() const { return t.key_comp(); }
value_compare value_comp() const { return t.key_comp(); } // 实际上和 key_comp 相同
iterator begin() const { return t.begin(); }
iterator end() const { return t.end(); }
reverse_iterator rbegin() const { return t.rbegin(); }
reverse_iterator rend() const { return t.rend(); }
bool empty() const { return t.empty(); }
size_type size() const { return t.size(); }
size_type max_size() const { return t.max_size(); }
void swap(set<Key, Compare, Alloc>& x) { t.swap(x.t); }
// --------------------
// 插入操作(使用 insert_unique,保证键唯一)
// --------------------
typedef pair<iterator, bool> pair_iterator_bool;
pair<iterator, bool> insert(const value_type& x) {
pair<typename rep_type::iterator, bool> p = t.insert_unique(x);
return pair<iterator, bool>(p.first, p.second);
}
iterator insert(iterator position, const value_type& x) {
typedef typename rep_type::iterator rep_iterator;
return t.insert_unique((rep_iterator&)position, x);
}
template <class InputIterator>
void insert(InputIterator first, InputIterator last) {
t.insert_unique(first, last);
}
// --------------------
// 删除操作
// --------------------
void erase(iterator position) {
typedef typename rep_type::iterator rep_iterator;
t.erase((rep_iterator&)position);
}
size_type erase(const key_type& x) {
return t.erase(x);
}
void erase(iterator first, iterator last) {
typedef typename rep_type::iterator rep_iterator;
t.erase((rep_iterator&)first, (rep_iterator&)last);
}
void clear() { t.clear(); }
// --------------------
// 查找与范围操作
// --------------------
iterator find(const key_type& x) const { return t.find(x); }
size_type count(const key_type& x) const { return t.count(x); }
iterator lower_bound(const key_type& x) const { return t.lower_bound(x); }
iterator upper_bound(const key_type& x) const { return t.upper_bound(x); }
pair<iterator, iterator> equal_range(const key_type& x) const {
return t.equal_range(x);
}
// --------------------
// 比较操作符
// --------------------
friend bool operator==(const set& x, const set& y) {
return x.t == y.t;
}
friend bool operator<(const set& x, const set& y) {
return x.t < y.t;
}
// 以下的 __STL_NULL_TMPL_ARGS 被定义为 <>
// 这里的写法是为了兼容旧式 C++ 编译器的模板语法
// 相当于 template<>,但写成宏方便统一修改
operator== __STL_NULL_TMPL_ARGS (const set&, const set&);
operator< __STL_NULL_TMPL_ARGS (const set&, const set&);
};
// 上面只是声明,下面才是定义
// 判断两个 set 是否相等(底层红黑树相等)
template <class Key, class Compare, class Alloc>
inline bool operator==(const set<Key, Compare, Alloc>& x,
const set<Key, Compare, Alloc>& y) {
return x.t == y.t;
}
// 判断一个 set 是否小于另一个 set(按底层红黑树的字典序比较)
template <class Key, class Compare, class Alloc>
inline bool operator<(const set<Key, Compare, Alloc>& x,
const set<Key, Compare, Alloc>& y) {
return x.t < y.t;
}
-
set 的底层容器是红黑树 (
rb_tree)-
元素按键值自动排序。
-
不允许重复(
insert_unique)。
-
-
迭代器是 const_iterator
- 不能修改元素值,因为键值就是实值,改了会破坏红黑树结构。
-
接口几乎都是转调用红黑树的方法
- 插入、删除、查找、范围查询等操作全部委托给
rb_tree。
- 插入、删除、查找、范围查询等操作全部委托给
-
identity 函数对象
- 用于把元素值直接当作键值返回。

浙公网安备 33010602011771号