数据离散化
题目
问题描述
离散化就是把无限空间(或非常大的空间)中有限的个体映射到有限的空间(较小空间)中去,以此提高算法的时空效率。
通俗的说,离散化就是在不改变数据相对大小的条件下,对数据进行相应的缩小。
栗子
原始数据:
8 999 91 100000 0000 15 999 91 。
离散化后:
1 3 4 2 3 。
离散化有什么用处呢?
有时候我们需要根据数值的大小开一个数组,由于数值很多,没办法开那么大的数组(大了会超内存限制的),但是数据的个数有限。
如:有500000个数字,他们的范围是0-2000000000,这样就满足离散化的条件。我们可以把这些数离散化,最小的缩小为1,大小相邻的依次增加1,这样所有的数的范围一定能缩小到1到500000之间,同时不改变相对大小关系。
任务
给你n个整数序列a[1],a[2],..,a[n]。请你在不改变相对大小关系的前提下进行离散化。
要求
1.离散化后,最小的数值为1。
2.原序列中相同的数,离散后还应相同。
3.大小相邻的两个数离散后相差为1。
4.离散后序列相对位置不变。
输入
第一行
n 原始数据的个数 。
第二行
n 个用一个空格隔开的整数 。
输出
一行
原序列离散后相应的序列 ,相邻两个数之间一个空格隔开。
输入输出样例
discretize.in | discretize.out |
8 99991 1000000000 15 99991 |
1 3 4 2 3 |
数据范围
30%的数据: n<100, 0< a[i]<1000;
100% 的数据: n<500000 0< a[i]<2000000000
第一种方法
思路
其实就是用一个辅助的数组把你要离散的所有数据存下来。
然后排序,排序是为了后面的二分。
去重,因为我们要保证相同的元素离散化后数字相同。
再用二分把离散化后的数字放回原数组。
注意事项
1.去重并不是把数组中的元素删去,而是重复的部分元素在数组末尾,去重之后数组的大小要减一
2.二分的时候,注意二分的区间范围,一定是离散化后的区间
3.如果需要多个数组同时离散化,那就把这些数组中的数都用数组存下来
核心代码
//n:原数组大小 //num:原数组中的元素 //lsh:离散化的数组 //cnt:离散化后的数组大小 int lsh[MAXN] , cnt , num[MAXN] , n; for(int i=1; i<=n; i++) { scanf("%d",&num[i]); lsh[i] = num[i]; } sort(lsh+1 , lsh+n+1); cnt = unique(lsh+1 , lsh+n+1) - lsh - 1; for(int i=1; i<=n; i++) num[i] = lower_bound(lsh+1 , lsh+cnt+1 , num[i]) - lsh;
第二种方法
思路
其实就是排序之后,枚举着放回原数组
用一个结构体存下原数和位置,按照原数排序
我结构体里面写了个重载,也可以写一个比较函数
最后离散化后数在rank[]里面
核心代码
struct Node { int data, id; bool operator < (const Node& a) const { return data < a.data; } }; Node num[MAXN]; int rank[MAXN], n; for (int i = 1; i <= n; i++) { scanf("%d", &num[i].data); num[i].id = i; } sort(num + 1, num + n + 1); for (int i = 1; i <= n; i++) { rank[num[i].id] = i; }