深入探索 C++ STL 中的 std::set
:特性、用法与高级技巧
在 C++ 标准模板库(STL)中,std::set
是一个非常强大且常用的关联式容器。它基于红黑树实现,具有自动排序和高效查找的特点。本文将深入探讨 std::set
的特性、基本用法以及一些高级技巧,帮助你更好地理解和使用这个容器。
1. std::set
的基本特性
std::set
是一个基于二叉树实现的关联式容器,具有以下特性:
-
自动排序:
std::set
中的元素会按照特定顺序自动排序,默认情况下是升序。 -
唯一性:
std::set
不允许重复元素。如果需要存储重复元素,可以使用std::multiset
。 -
高效查找:由于基于红黑树实现,
std::set
的查找、插入和删除操作的时间复杂度均为 O(log n)。
2. 基本用法
2.1 构造与初始化
std::set
的构造方式非常灵活。以下是一些常见的构造方法:
cpp复制
#include <iostream>
#include <set>
int main() {
// 默认构造
std::set<int> s1;
s1.insert(4);
s1.insert(1);
s1.insert(3);
// 拷贝构造
std::set<int> s2(s1);
// 使用初始化列表构造
std::set<int> s3 = {1, 2, 3, 4};
// 打印集合内容
for (int num : s3) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
2.2 插入与删除
std::set
提供了高效的插入和删除操作。插入操作会自动将元素按顺序插入到合适的位置,而删除操作则会移除指定的元素。
cpp复制
#include <iostream>
#include <set>
void printSet(const std::set<int>& s) {
for (int num : s) {
std::cout << num << " ";
}
std::cout << std::endl;
}
int main() {
std::set<int> s;
s.insert(4);
s.insert(1);
s.insert(3);
std::cout << "After insert: ";
printSet(s);
s.erase(3); // 删除元素 3
std::cout << "After erase: ";
printSet(s);
s.clear(); // 清空集合
std::cout << "After clear: ";
printSet(s);
return 0;
}
2.3 查找与统计
std::set
提供了高效的查找功能,可以通过 find
方法查找特定元素,也可以通过 count
方法统计元素的出现次数。
cpp复制
#include <iostream>
#include <set>
int main() {
std::set<int> s = {1, 2, 3, 4};
auto it = s.find(3);
if (it != s.end()) {
std::cout << "Found: " << *it << std::endl;
} else {
std::cout << "Not found" << std::endl;
}
// 统计元素出现次数
std::cout << "Count of 3: " << s.count(3) << std::endl;
std::cout << "Count of 5: " << s.count(5) << std::endl;
return 0;
}
3. 高级用法
3.1 自定义排序规则
默认情况下,std::set
使用 <
运算符对元素进行升序排序。然而,我们可以通过自定义比较函数来改变排序规则。
cpp复制
#include <iostream>
#include <set>
struct Compare {
bool operator()(int a, int b) const {
return a > b; // 降序排序
}
};
int main() {
std::set<int, Compare> s = {1, 2, 3, 4};
for (int num : s) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
3.2 使用 std::multiset
如果需要存储重复元素,可以使用 std::multiset
。它与 std::set
的用法类似,但允许重复元素。
cpp复制
#include <iostream>
#include <set>
void printMultiset(const std::multiset<int>& ms) {
for (int num : ms) {
std::cout << num << " ";
}
std::cout << std::endl;
}
int main() {
std::multiset<int> ms;
ms.insert(1);
ms.insert(1);
ms.insert(2);
ms.insert(3);
printMultiset(ms);
// 删除指定元素
ms.erase(1);
printMultiset(ms);
return 0;
}
3.3 配合 std::pair
使用
std::set
也可以存储复杂的数据类型,例如 std::pair
。通过自定义比较函数,可以实现对 std::pair
的排序。
cpp复制
#include <iostream>
#include <set>
struct PairCompare {
bool operator()(const std::pair<int, int>& a, const std::pair<int, int>& b) const {
return a.first < b.first; // 按第一个元素排序
}
};
int main() {
std::set<std::pair<int, int>, PairCompare> s;
s.insert({1, 2});
s.insert({2, 3});
s.insert({1, 4});
for (const auto& p : s) {
std::cout << "{" << p.first << ", " << p.second << "} ";
}
std::cout << std::endl;
return 0;
}
4. 性能分析
std::set
基于红黑树实现,因此其插入、删除和查找操作的时间复杂度均为 O(log n)。这使得它在处理大量数据时仍然保持高效。然而,由于红黑树的维护成本较高,std::set
的性能可能不如基于哈希表的容器(如 std::unordered_set
)。在选择容器时,需要根据具体需求权衡。
5. 总结
std::set
是一个功能强大且高效的关联式容器,适用于需要自动排序和高效查找的场景。通过自定义比较函数,可以灵活地调整排序规则。同时,std::multiset
提供了对重复元素的支持,进一步扩展了其应用场景。
希望本文能帮助你更好地理解和使用 std::set
。如果你有任何问题或建议,欢迎在评论区留言讨论!