学习笔记:离散化
引入
离散化,就是当我们只关心数据的大小关系时,用排名代替原数据进行处理的一种预处理方法。离散化把无限空间中有限的个体映射到有限的空间中去,以此提高算法的时空效率,即:在不改变数据相对大小的条件下,对数据进行相应的缩小。本质上是一种哈希,它在保持原序列大小关系的前提下把其映射成正整数。当原数据很大或含有负数、小数时,难以表示为数组下标,一些算法和数据结构无法运作,这时我们就可以考虑将其离散化。
实际上,离散化可以用 STL 较简单地完成。
实现
首先,我们假设有这样一个序列:\left\{1,2023,114514,1919810,1145141919810\right\}。
如果要对这个序列进行桶排序的话,通常情况下需要 1145141919810 个桶!如果强行开这么大的数组的话:
tsqtsqtsq@MateBook:/mnt/c/Users/tsqtsqtsq/OIer/work$ g++ -o a lisan.cpp -O2 -std=c++14 -static
/usr/bin/ld: failed to convert GOTPCREL relocation; relink with --no-relax
collect2: error: ld returned 1 exit status
tsqtsqtsq@MateBook/mnt/c/Users/tsqtsqtsq/OIer/work$ 
那应该是会寄的……
显然这里面有大量空间是被浪费掉的。
但如果我们考虑离散化的话,就只需要 5 个桶!
tsqtsqtsq@MateBook:/mnt/c/Users/tsqtsqtsq/OIer/work$ g++ -o a lisan.cpp -O2 -std=c++14 -static
tsqtsqtsq@MateBook:/mnt/c/Users/tsqtsqtsq/OIer/work$ 
那就可以正常编译了!
那么如何实现离散化呢?
首先,我们显然还是需要原数据的,所以先复制一份:
for(int i = 1 ; i <= n ; i ++)b[i] = a[i]; // 将原数组复制一份
接着我们再排序并去重:
    sort(b + 1, b + n + 1); // 排序
    len = unique(b + 1, b + n + 1) - b - 1; // 去重,其中 len 为不重复元素的数量
std::unique() 的返回值是一个迭代器(对于数组来说就是指针了),它表示去重后容器中不重复序列的最后一个元素的下一个元素。所以可以这样作差求得不重复元素的数量。
再用一个数组,储存 a 中每个元素在 b 中的排名:
    for(int i = 1 ; i <= n ; i ++)c[i] = lower_bound(b, b + len, a[i]) - b; // 查找离散化之后的位置
这样我们就实现了原序列的离散化。
因为排序和 n 次二分查找的复杂度都是 O(n\log n) ,所以离散化的复杂度也是 O(n\log n)。完整代码很短:
输出结果如下:
tsqtsqtsq@MateBook:/mnt/c/Users/tsqtsqtsq/OIer/work$ g++ -o a lisan.cpp -O2 -std=c++14 -static
tsqtsqtsq@MateBook:/mnt/c/Users/tsqtsqtsq/OIer/work$ time ./a
5
1 2023 114514 1919810 1145141919810
1 2 3 4 5
real    0m0.263s
user    0m0.000s
sys     0m0.023s
tsqtsqtsq@MateBook:/mnt/c/Users/tsqtsqtsq/OIer/work$ time ./a
10
-1145141919810 -1919810 -114514 -100 1 2 3 114514 1919810 1145141919810
1 2 3 4 5 6 7 8 9 10
real    0m0.256s
user    0m0.000s
sys     0m0.023s
tsqtsqtsq@MateBook:/mnt/c/Users/tsqtsqtsq/OIer/work$
离散化也不一定要从小到大排序,有时候也需要从大到小。这时在排序和查找时相应地加上 greater<int>()

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号