常用技巧 —— 离散化

【概述】

离散化是数据结构中的一个常用技巧,其可以有效的降低时空复杂度,其基本思想就是在众多可能的情况中,只去考虑需要用到的值,通过离散化,可以改进低效的算法,甚至实现根本不可能实现的算法。

对于一些数量较少,但数值较大或者可能出现负数这种难以处理的数据,自身无法作为数组的下标保存对应的属性,如果这时只是需要这些数据的相对属性, 那么可以对其进行重新赋值,即进行离散化处理。

简单来说,对于 n 个数据,当 n 很小而每个数据 a[i] 的数据范围很大时,就可以考虑离散化为更小的值,将他们重新赋值为 [1,n] 之间的数据,从而实现更多的算法。

例如:有 1E5 个数,每个数的大小不超过 1E9,要对这些数进行某些操作(并查集等),那么肯定不能直接开 1E9 大小的数组,但是 1E5 的范围就完全没问题,也就是说,当不需要这些数据具体是多少时,只需要知道他们的相对大小

离散化分为两种,一种是重复的元素离散化后数值仍相同,一种则是重复的元素离散化后数值不同。

【STL+二分实现离散化】

使用 STL+二分 实现离散化,重复的元素数值仍相同,其实质就是利用一个辅助数组将要离散的数据保存下来,然后进行排序去重,最后再用二分将离散数据的位置放回原数组。

注:

  • 去重并不是将数组中重复的元素删除,而是将重复的元素放在数组末尾
  • 二分时,要注意二分的区间范围一定是离散化后的区间

例如:对于数组 {6,8,4,9,5,6,7,4},在排序后得到 {4,4,5,6,6,7,8,9},去重后得到 {4,5,6,7,8,9},经过二分后,原序列变为 {3,5,1,6,2,3,4,1}

int temp[N],a[N];
int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        temp[i]=a[i];//辅助数组临时存储
    }
    sort(temp+1,temp+n+1);//排序,便于去重
    int len=unique(temp+1,temp+n+1)-temp-1;//去重,len为去重后数组长度
    for(int i=1;i<=n;i++)//a[i]即为离散化后的数组
        a[i]=lower_bound(temp+1,temp+len+1,a[i])-temp;
}

【用数组离散】

使用数组实现离散化,重复的元素数值不同,其直接用结构体存储原序列元素的位置,经过排序后将他们重新赋值,最后将结果存放在 rank 数组中。

例如:

对于序列 {3,6,5,10,8},其初始值和 id 为: \left\{\begin{matrix}val=\{{3,6,5,10,8}\} \\ id=\{1,2,3,4,5\} \end{matrix}\right.

经过排序后:\left\{\begin{matrix}val=\{{3,5,6,8,10}\} \\ id=\{1,3,2,5,4\} \end{matrix}\right.

进行离散化:\left\{\begin{matrix}val=\{{3,5,6,8,10}\} \\ id=\{1,3,2,5,4\}\\rank=\{1,2,3,4,5\} \end{matrix}\right.

再按照原来的顺序进行排列:\left\{\begin{matrix}val=\{{3,5,6,8,10}\} \\rank=\{1,3,2,5,4\} \end{matrix}\right.

此时,rank 数组就是离散化后的值

struct Node{
    int val,id;
    bool operator < (const Node &rhs)const{//按值排序
        return val>n;
    for(int i=1;i<=n;i++){
        cin>>a[i].val;
        a[i].id=i;//记录序号
    }
    sort(a+1,a+n+1);//按值排序
    for(int i=1;i<=n;i++)//进行映射
        rank[a[i].id]=i;
}

【例题】

  • 程序自动分析(洛谷-P1955)(离散化+并查集):点击这里
  • Making the Grade(POJ-3666)(离散化思想+线性DP):点击这里
  • Mindis(HDU-6670)(离散化+网格图建图+bfs):点击这里
  • Parity game(POJ-1733)(离散化+带权并查集区间问题):点击这里

你可能感兴趣的:(#,常用技巧——离散化,——常用技巧——)