转载请注明出处:http://www.cnblogs.com/kevince/p/3893531.html ——By Kevince
最近做了一些需要离散数据的题目,比如URAL 1019 以及POJ 2528等,由于数据较大,如果用传统的方法建立对应的数据结构消耗的内存和时间肯定是不能被接受的。由于以前没有怎么接触过需要离散化的题目,于是就通过自己最近的做题经验以及网上的部分资料,整理并讲解了常用的离散数据的方法。
何为离散化?离散化,就是把无限空间中有限的个体映射到有限的空间中去,以此提高算法的时空效率。
比如给你n个数:98998988,32434234,433234556,32434234,8384733,……
让你统计其中每个数出现的次数,传统的做法有好几种,比如一遍一遍的扫过去,比对叠加,这样算法的效率是O(n2),效率低下;
再比如先排序,再统计连续的相同的个数,这里的效率已经有所提高了,不过假如上面的数据是一道线段树的题目给出的数据,那么建树需要的空间开销实在是太大了。
再改进一下,采用哈希的方法,开一个大于其中最大数的数组并初始化为零,O(n)扫一下,在该数字对应的下标的元素上+1,如果对于比较小的数字还好说,但是对于上面出现的数字直接采用哈希对空间的开销是十分大的也是没有必要的,所以这里用到了数据的离散化。
首先将数字排序:32434234,32434234,43324556,8384733,98998988
去重后给予其对应的索引:0,0,1,2,3分别对应每个数,就可以简化很多操作,减少了很多不必要的资源开销。
除了对于较大整数需要使用离散化之外,对于一些需要使用整型数据结构,但给出的数据却是小数的也可以使用离散化,将其索引为整数就可以了。
那么可以总结出离散化的步骤:
1、排序
2、去重
3、索引
为了简化代码,我们采用STL算法离散化:
int a[n], b[n], sub[n]; //a[n]是即将被离散化的数组,b[n]是a[n]的副本,sub用于排序去重后提供离散化后的值 sort(sub, sub + n); int size = unique(sub, sub + n) - sub; for(int i = 0; i < n; i++) a[i] = lower_bound(sub, sub + size, a[i]) - sub; //即a[i]为b[i]离散化后对应的值
对于第3步,若离散化后序列为0, 1, 2, ..., size - 1则用lower_bound,从1, 2, 3, ..., size则用upper_bound,其中lower_bound返回第1个不小于b[i]的值的指针,而upper_bound返回第1个大于b[i]的值的指针,当然也可以用lower_bound然后再加1得到与upper_bound相同结果,两者都是针对以排好序列。使用STL离散化大大减少了代码量且结构相当清晰。
离散化后查询的问题(采用的如上代码的离散方式):
1、通过离散后的值查询离散前的值:
若离散后的值为x,那么对应的离散前的值为sub[x];
2、通过离散前的下标查询离散后的值:
若离散前的下标为i,那么对应的离散后的值为a[i];
3、通过离散前的值查询离散后的值:
如果没有相应的保存的话,首先要确定y在sub[]中的位置,或者在b[]中的位置,前者可以使用pos = lower_bound(sub, sub+size, y) - sub。那么pos即为下标,通过第二步再查询就好了。
感觉STL算法的离散是最实用的,代码量最小的,适合竞赛的时候使用,若还有更加简便高效的方法,我肯定还会再将其整理上来。
参考资料:http://blog.csdn.net/gokou_ruri/article/details/7723378