集合与关联数组

例题

例题 - Distinct Numbers
CSES - 普及-
例题 - 请努力完成本题,再阅读以下内容。解答

有序集合std::set

在有序集合中,元素是按顺序排序好的。插入、删除、查找操作的复杂度都是 O(logn)\mathcal{O}(\log n)。有序集合的实现是std::set, 头文件是<set>

set和数组(包括vector)一样,都是保存一批数据的数据结构。但有一系列不同的地方:

  1. 数组读写访问每个元素的时间复杂度是O(1)\mathcal{O}(1),而setO(logn)\mathcal{O}(\log n)
  2. set保证元素的值的唯一性,多次插入同一个值,只会保留一个,而数组没有这个功能。
  3. set保证元素的有序性,插入的数据会自动按顺序排列。
  4. 数组可以按下标随机访问数据,而set不能。

set可以通过迭代器进行遍历:

set<int> s;
s.insert(1);
s.insert(3);
s.insert(2);
for (auto it = s.begin(); it != s.end(); it++)
    printf("%d\n", *it);

set中查找和删除元素,使用s.find(x)s.erase(x)方法,前者返回指向对应元素的迭代器(如果存在),或者s.end()(如果找不到)。

答案 - Distinct Numbers

这里要求我们统计n个整数中一共有多少个不同的数,我们可以用set来完成这个任务。

#include <bits/stdc++.h>
using namespace std;
 
int n;
set<int> s;
 
int main() {
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        int a;
        scanf("%d", &a);
        s.insert(a);        // 如果有重复的元素,在set中只会保留一个。
    }
    printf("%d", s.size());
    return 0;
}

无序集合std::unordered_set

set是有序的元素集合,如果问题不需要元素间有顺序,也可以使用std::unordered_set,性能比set更高一些(约2倍速度)。

set在内部使用二叉搜索树实现,而unordered_set使用哈希实现,需要元素有对应的哈希函数。

关联数组std::map

关联数组(map)是一组 (key)和(value)的列表,要求是唯一的,不能有重复,而可以有重复。map支持以下操作:

map的键也是排序的,如果不需要键有序,也可以使用unordered_map

map的遍历

map<int,int> m;
m[1] = 1;
m[2] = 4;
m[3] = 9;
for (const auto &x: m)  // 标准遍历方法
    printf("%d -> %d\n", x.first, x.second);
for (auto x: m)         // 也可以,但x会做一份复制,稍慢,而且修改x并不会修改map
    printf("%d -> %d\n", x.first, x.second);

多重集合std::multiset和多重关联数组std::multimap

setmap经常使用的另外一个变体是multisetmultimap,区别是multiset允许同一个值存在多个, 而multimap允许同一个键对应多个值。使用这两个数据结构时,有以下经常要注意的地方:

习题

习题
P5832USACO BronzeWhere am I?2019普及-解答