一、什么是 std::set?
std::set 是一种存储唯一元素并自动排序的容器,其特点是:
- 元素自动按特定顺序排列(默认升序)
- 不允许重复元素
- 基于红黑树实现,支持高效的插入、删除和查找操作(时间复杂度 O (log n))
如果你需要一个自动排序且无重复元素的集合,std::set 会是理想选择。
二、快速上手:基本用法
1 头文件
#include <set>
2 初始化与创建
// 创建一个空的 set(默认升序排序)
std::set<int> numbers;
// 使用初始化列表创建
std::set<int> primes = {2, 3, 5, 7, 11};
// 创建降序排序的 set
std::set<int, std::greater<int>> descendingSet = {5, 2, 8, 1};

3 insert 插入元素
std::set<std::string> fruits;
// 插入单个元素
fruits.insert("apple");
fruits.insert("banana");
// 插入多个元素
fruits.insert({"cherry", "date", "elderberry"});
// 插入重复元素(会被自动忽略)
auto result = fruits.insert("apple");
if (!result.second) {
std::cout << "苹果已经存在于集合中" << std::endl;
}

insert 方法返回一个 pair 对象,其中 second 是布尔值,表示插入是否成功。
pair<iterator,bool> insert (const value_type& val);
4 find/count 查找元素
// 查找元素
auto it = fruits.find("banana");
if (it != fruits.end()) {
std::cout << "找到元素: " << *it << std::endl;
}
else {
std::cout << "未找到元素" << std::endl;
}
if (fruits.count("banana")) {
std::cout << "找到元素" << fruits.count("banana") << std::endl;
}
else if (!fruits.count("banana")){
std::cout << "未找到元素" << fruits.count("banana") << std::endl;
}

5 erase 删除元素
// 按值删除
size_t removed = fruits.erase("date");
if (removed > 0) {
std::cout << "成功删除元素" << std::endl;
}
// 按迭代器删除
auto it = fruits.find("cherry");
if (it != fruits.end()) {
fruits.erase(it);
}
// 清空所有元素
fruits.clear();
6 遍历元素
// 正向遍历
std::cout << "正向遍历: ";
for (const auto& fruit : fruits) {
std::cout << fruit << " ";
}
std::cout << std::endl;
// 使用迭代器遍历
std::cout << "迭代器遍历: ";
for (auto it = fruits.begin(); it != fruits.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
// 反向遍历
std::cout << "反向遍历: ";
for (auto it = fruits.rbegin(); it != fruits.rend(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;

三、unordered_set
std::unordered_set 是 C++11 引入的**无序关联容器,**如果你需要快速判断一个元素是否存在,且不关心元素的顺序,std::unordered_set 会是比 std::set 更好的选择。
在用法上两者基本没有差别,但要特别注意两者在结构的不同:
| 特性 | std::unordered_set | std::set |
|---|---|---|
| 顺序 | 无序 | 有序(默认升序) |
| 底层实现 | 哈希表 | 红黑树 |
| 查找效率 | 平均 O (1),最坏 O (n) | O(log n) |
| 插入 / 删除效率 | 平均 O (1),最坏 O (n) | O(log n) |
| 内存占用 | 较高(哈希表开销) | 较低 |
std::unordered_set 是一个以空间换时间的高效容器,特别适合需要频繁查找操作的场景。它提供了平均 O (1) 时间复杂度的基本操作,大大优于 std::set 的 O (log n)。
对于大多数查找密集型应用,std::unordered_set 通常是更好的选择。
四、进阶用法:自定义类型与比较器
std::set 不仅可以存储基本数据类型,还能存储自定义类型。这时我们需要定义排序规则。
#include <set>
#include <string>
#include <iostream>
// 定义一个 Person 结构体
struct Person {
std::string name;
int age;
// 打印信息的方法
void print() const {
std::cout << name << " (" << age << "岁)";
}
};
// 自定义比较器:按年龄升序排序
struct CompareByAge {
bool operator()(const Person& a, const Person& b) const {
return a.age < b.age;
}
};
int main() {
// 使用自定义比较器创建 set
std::set<Person, CompareByAge> people;
// 插入元素
people.insert({"Alice", 30});
people.insert({"Bob", 25});
people.insert({"Charlie", 35});
// 遍历输出
for (const auto& person : people) {
person.print();
std::cout << std::endl;
}
return 0;
}

如果需要按姓名排序,只需修改比较器:
struct CompareByName {
bool operator()(const Person& a, const Person& b) const {
return a.name < b.name; // 按姓名字典序排序
}
};

五、注意事项
std::set 中的元素是常量,不能直接修改。如需修改,需先删除旧元素再插入新元素:
// 错误做法 - 不能直接修改
auto it = numbers.find(5);
if (it != numbers.end()) {
// *it = 6; // 编译错误!元素是只读的
}
// 正确做法
numbers.erase(it);
numbers.insert(6);
对于自定义类型,必须定义严格弱序(Strict Weak Ordering)的比较规则,否则行为未定义避免在循环中频繁插入删除元素,这会导致迭代器失效
2342

被折叠的 条评论
为什么被折叠?



