离散化,就是当我们只关心数据的大小关系时,用排名代替原数据进行处理的一种预处理方法。离散化本质上是一种哈希,它在保持原序列大小关系的前提下把其映射成正整数。当原数据很大或含有负数、小数时,难以表示为数组下标,一些算法和数据结构(如BIT)无法运作,这时我们就可以考虑将其离散化。
在之前关于树状数组的笔记中,我提到过离散化,但其实那时我所用的方法并不是很合理,因为它会把序列中相同的数映射为不同的排名。而实际上,离散化可以用STL较简单地完成。
例如,现在我们有序列A=[10, 23, 35, 3, -40, 3]。我们先复制一个同样的序列:
int C[MAXN];
memcpy(C, A, sizeof(A));
排序,去重:
sort(C, C + n);
int l = unique(C, C + n) - C; // l为不重复元素的数量
std::unique()的返回值是一个迭代器(对于数组来说就是指针了),它表示去重后容器中不重复序列的最后一个元素的下一个元素。所以可以这样作差求得不重复元素的数量。现在我们有C=[-40, 3, 10, 23, 35]。
再用一个数组,储存A中每个元素在C中的排名:
int L[MAXN];
for (int i = 0; i < n; ++i)
L[i] = lower_bound(C, C + l, A[i]) - C + 1; // 二分查找
这样我们就实现了原序列的离散化。得到L=[3, 4, 5, 2, 1, 2]。
因为排序和n次二分查找的复杂度都是
,所以离散化的复杂度也是
。完整代码很短:
int C[MAXN], L[MAXN];
// 在main函数中...
memcpy(C, A, sizeof(A)); // 复制
sort(C, C + n); // 排序
int l = unique(C, C + n) - C; // 去重
for (int i = 0; i < n; ++i)
L[i] = lower_bound(C, C + l, A[i]) - C + 1; // 查找
离散化也不一定要从小到大排序,有时候也需要从大到小。这时在排序和查找时相应地加上greater<int>()
就可以了。
Pecco:算法学习笔记(目录)zhuanlan.zhihu.com