关联容器 - 有序集合
set 是基于红黑树实现的有序集合,特点是:
#include <set>
using namespace std;
int main() {
// 创建 set
set s;
// 插入元素
s.insert(5);
s.insert(3);
s.insert(7);
s.insert(3); // 重复元素不会被插入
// s: {3, 5, 7}
// 查找元素
if (s.find(5) != s.end()) {
cout << "找到 5" << endl;
}
if (s.count(10)) { // count 返回 0 或 1
cout << "存在 10" << endl;
}
// 删除元素
s.erase(5); // 删除值为5的元素
// 遍历
for (int x : s) {
cout << x << " "; // 3 7
}
// 大小操作
cout << s.size() << endl; // 2
cout << s.empty() << endl; // false
s.clear();
return 0;
}
输入数字,观察 set 如何自动排序和去重:
set 为空
set s = {1, 3, 5, 7, 9};
// 最小和最大元素
cout << *s.begin() << endl; // 1 (最小)
cout << *s.rbegin() << endl; // 9 (最大)
// 大于等于某个值的第一个元素
auto it = s.lower_bound(4); // 指向 5
if (it != s.end()) {
cout << *it << endl; // 5
}
// 大于某个值的第一个元素
it = s.upper_bound(5); // 指向 7
if (it != s.end()) {
cout << *it << endl; // 7
}
// 删除范围内的元素
s.erase(s.lower_bound(3), s.upper_bound(7));
// s: {1, 9}
// 降序排列
set> s = {1, 3, 5, 7, 9};
// s: {9, 7, 5, 3, 1}
// 自定义比较函数
struct cmp {
bool operator()(int a, int b) const {
return a % 10 < b % 10; // 按个位数排序
}
};
set s2 = {13, 22, 35, 41};
// s2: {41, 22, 13, 35} (个位数 1, 2, 3, 5)
// 存储 pair
set> s;
s.insert({1, 2});
s.insert({2, 1});
s.insert({1, 2}); // 重复,不会插入
// 存储 pair 时,先比较 first,再比较 second
// {1, 2} < {1, 3} < {2, 1}
// 存储 pair 时常用技巧
typedef pair PII;
// 排序规则:按 first 升序,first 相同按 second 降序
set> s2([](PII a, PII b) {
if (a.first != b.first)
return a.first < b.first;
return a.second > b.second;
});
s2.insert({1, 3});
s2.insert({1, 2});
s2.insert({2, 1});
// s2: {{1, 3}, {1, 2}, {2, 1}}
multiset 与 set 类似,但允许重复元素。
#include <set>
using namespace std;
int main() {
multiset ms;
// 插入元素(允许重复)
ms.insert(3);
ms.insert(1);
ms.insert(3);
ms.insert(2);
ms.insert(3);
// ms: {1, 2, 3, 3, 3}
// count 返回元素出现次数
cout << ms.count(3) << endl; // 3
// find 返回第一个匹配的迭代器
auto it = ms.find(3); // 指向第一个 3
// 删除指定值的所有元素
ms.erase(3);
// ms: {1, 2}
// 删除指定位置的元素
it = ms.begin();
ms.erase(it); // 删除第一个元素
// ms: {2}
// 删除指定位置的某个元素
it = ms.find(2);
ms.erase(it, ms.end()); // 删除从 it 到 end 的元素
return 0;
}
| 操作 | set/multiset | unordered_set |
|---|---|---|
| 插入 | $$ O(\log n) $$ | $$ O(1) $$ |
| 删除 | $$ O(\log n) $$ | $$ O(1) $$ |
| 查找 | $$ O(\log n) $$ | $$ O(1) $$ |
| 区间查询 | $$ O(\log n) $$ | 不支持 |
| 顺序遍历 | 有序 | 无序 |
vector v = {1, 2, 2, 3, 3, 3, 4};
// 方法1:使用 set
set s(v.begin(), v.end());
vector result(s.begin(), s.end());
// result: {1, 2, 3, 4}
// 方法2:使用 unique(需要先排序)
sort(v.begin(), v.end());
v.erase(unique(v.begin(), v.end()), v.end());
// v: {1, 2, 3, 4}
set scores = {60, 65, 70, 75, 80, 85, 90, 95};
// 查找大于等于75的最低分
auto it = scores.lower_bound(75);
cout << *it << endl; // 75
// 查找大于75的最低分
it = scores.upper_bound(75);
cout << *it << endl; // 80
// 统计区间内的元素数量
int start = 70, end = 85;
int count = distance(scores.lower_bound(start), scores.upper_bound(end));
cout << count << endl; // 4 (70, 75, 80, 85)
vector maxSlidingWindow(vector& nums, int k) {
multiset ms;
vector result;
for (int i = 0; i < nums.size(); i++) {
ms.insert(nums[i]);
if (i >= k) {
// 移除滑出窗口的元素
ms.erase(ms.find(nums[i - k]));
}
if (i >= k - 1) {
// 当前窗口的最大值
result.push_back(*ms.rbegin());
}
}
return result;
}