离散化目的:
将原来值很大的一组数据更新为一组不改变原来数值之间相对关系同时数值很小的一组数据。
此操作的前提为接下来的操作与数值大小无关,仅与相对关系有关。
思路一(结构体重构):
1.先在结构体内设置两个量:
val,数值
ord,输入顺序
再创建一个结构体数组
2.对结构体数组对数值大小进行排序,可直接调用函数
再将val进行更新;
对于数值来说,可能存在重复的值,故不建议直接用元素下标直接代替val的值,可以安排一个cnt,仅当前一个元素的数值与当前元素的值不同时用(++cnt)来更新,否则只有cnt来更新
更新结果可以直接存入另一个数组,而不直接在原数组中修改,否则有一些细节需要考虑,容易出错
3.输出检验
实现代码:
#include<bits/stdc++.h>
using namespace std;
int n;
struct node{
int val;
int ord;
};
const int MAXN=100005;
node a[MAXN];
int b[MAXN];
int cmp(node x,node y){
return x.val<y.val;
}
int CMP(node x,node y){
return x.ord<y.ord;
}
int main(){
cin>>n;
for(int i=0;i<n;i++){
a[i].ord=i;
cin>>a[i].val;
}
sort(a,a+n,cmp);
int cnt=n;
b[a[0].ord]=0;
for(int i=1;i<n;i++){
if(a[i].val!=a[i-1].val){
b[a[i].ord]=++cnt;
}
else b[a[i].ord]=cnt;
}
for(int i=0;i<n;i++)
cout<<b[i]<<" ";
}
思路二(二分索引):
对于给定的序列进行离散化处理,除了上方的操作,还可以借助STL的函数,构造出一个映射关系,使得原序列中的可以映射到小区间中的某一位。
1.将原数据复制到操作数组中,可以通过vector储存,也可以使用普通数组。
2.对操作数组进行排序,再进行去重;
去重:原数组中可能存在重复的数据,但离散化后我们要保证其对应的离散化的结果仍然一致
3.二分索引,直接原数据在操作数组中找到对应的位置下标,以下标作为离散化结果。
去重具体实现:
借助两个函数unique跟erase
unique:
end_unnique = unique( result.begin(), result.end())
返回值为数组去重后重复区间的首地址;
去重原理为将重复元素后移,从而使得非重复元素聚集在数组的前部。
erase:
无返回值,可见于以vector存储的写法中;
vector可以通过函数得知数组大小,去重之后需要抹除重复部分,更新vector大小,以防在二分索引中出现错误。
若为数组储存,则通过
x = unique(a , a + n ) - a 得知非重复区间的大小。
vector<int> alls; // 存储所有待离散化的值
sort(alls.begin(), alls.end()); // 将所有值排序
alls.erase(unique(alls.begin(), alls.end()), alls.end()); // 去掉重复元素
索引具体实现:
默认离散化结果是从0开始的。
那么在操作数组中找到第一个大于或等于原数据的位置的下标就是我们离散化的结果,可以通过函数lower_bound直接实现。
几种二分查找的函数:
lower_bound:该函数会返回第一个大于等于所查询元素的元素的位置,如果没有这样的位置,则返回一个下标i,使得在i处插入元素v后,序列仍然有序;
upper_bound:该函数会返回第一个大于所查询元素的元素的位置,如果没有这样的位置,则返回一个下标i,使得在i处插入元素v后,序列仍然有序;
binary_search:在一个有序序列中查找元素出现的位置,查找成功返回位置下标,查找失败返回-1;
vector<int> alls;
for(int i=0;i<n;i++) a[i]=lower_bound(alls.begin(),alls.end(),a[i])-alls.begin() + 1;
实现代码:
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int a[N];
vector<int> alls; // 存储所有待离散化的值
int n;
int main(){
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i];
alls.push_back(a[i]);
}
sort(alls.begin(), alls.end()); // 将所有值排序
alls.erase(unique(alls.begin(), alls.end()), alls.end()); // 去掉重复元素
// 二分求出x对应的离散化的值
// 找到第一个大于等于x的位置
//从0开始计数
for(int i=0;i<n;i++) a[i]=lower_bound(alls.begin(),alls.end(),a[i])-alls.begin();
for(int i=0;i<n;i++)cout<<a[i]<<" ";
}
/*
9
90 80 70 60 60 50 40 30 40
*/