离散化
离散化
什么是离散化?我们在什么情况下能用离散化呢?
离散化,顾名思义,把无限空间中有限的个体映射到有限的空间中去,以此提高算法的时空效率。通俗的说,离散化是在不改变数据相对大小的条件下,对数据进行相应的缩小。
那么在什么情况下能用离散化呢?这里举出一个例子,来帮助大家来理解999 1000 222 234 987
经过离散化后是4 5 1 2 3
,实际上就是从小到大排序后的“第数”,即第几个。
那么在转化为“第数”后,那么实际上我们在做实际问题时,原数的值在离散化后已经被转化成“第数”,原数的值已经不重要了。所以对于只需要去关注大小关系,而不去关注原本数值的题,离散化就非常合适。
离散化的两种方式
第一种,我们利用函数unique函数和lower_bound函数,能够处理有重复数据的情况。
第二种,我们利用一个结构体,不能够处理有重复数据的情况。
第一种
unique函数的应用
unique 参数:_ForwardIterator __first, _ForwardIterator __last 即两个迭代器。
unique是STL的一个非常实用的函数,用途是将迭代器所示的左闭右开的区间(将*_first到*_last-1)去重,说是去重,实际上是把后面的元素前移,替代那些重复的元素,使重复的元素被不重复的元素所替代,从而达到去重的目的。但是前移后,不重复的元素在其原来的位置的值没有变化,如果你不理解这句话的含义,我们来举个例子:
9 2 2 5 5 4 7 8 10
在去重后应该是“9 2 5 4 7 8 10 8 10”,也就是说,8,10虽然前移,但它们原本的位置上,原本的值并没有改变。
注意这里的unique,
我们在用之前一般都排序,原因是lower_bound函数的对象是一个有序的函数或容器,不要忘记!
unique在去重的同时返回一个迭代器,它指向的是去重后容器中不重复序列的最后一个元素的下一个元素。例如上面那个例子,返回的值应该是第二个8这个元素所对应的迭代器(10的后面)。
lower_bound的应用
lower_bound 参数:_ForwardIterator __first, _ForwardIterator __last, const _Tp& __val
首先开头两个还是迭代器,后面的是一个值,用途是在这个迭代器所示区间(同样左闭右开)里找第一个大于或等于该值的数字,如果这个序列是排好序的,那么这个函数的作用实际上就是返回这个元素在这个有序的序列里第一次出现的位置,例如这个例子1 2 2 5 5 4 7 8 10
,那么如果引用函数lower_bound(begin,end,4),返回的值应该是第一个5这个位置所对应的迭代器。
代码
int n;
int a[N],t[N];
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i],t[i]=a[i];
// for(int i=1;i<=n;i++) cout<<t[i]<<" ";
// cout<<endl;
sort(t+1,t+n+1);
int m=unique(t+1,t+n+1)-t-1;
for(int i=1;i<=n;i++) a[i]=lower_bound(t+1,t+m+1,a[i])-t;
// for(int i=1;i<=n;i++) cout<<a[i]<<" ";
}
这里稍微解释一下这个代码,例如我们输入的数据是5 4 5 1 2 2 ,那么t数组应该是0 4 5 1 2 2 (数组下标为i,这里0<=i<=n)sort后应该变成了0 1 2 2 4 5(0<=i<=n)经过unique后t变为0 1 2 4 5 5,m的值应该是4,那么这里怎么理解呢,我们可以简单的把t理解成头元素的迭代器,即begin,注意,这里的头元素迭代器begin应该指的是t[0]的迭代器,迭代器减去迭代器返回的却是一个int型的数?我们这么理解,假设unique返回的迭代器为483xxx797,begin的迭代器为483xxx791,则相减得到的结果应该是6,这里我们要减去的是区间开始的迭代器,即t+1,因为t0我们没有把它算在内。
然后在1至1+m这个左闭右开的区间中找a[i],因为他返回的是一个迭代器,所以我们依然减去迭代器,但这里并不是减去区间开始的迭代器,这里的目的是把这个迭代器转化成一个int型的,此时a[i]存储的是a[i]在t数组里的位置。
运行完后,a数组里即是离散化结果。
int n;
int a[N],t[N];
int main()
{
cin>>n;
for(int i=0;i<n;i++) cin>>a[i],t[i]=a[i];
// for(int i=0;i<n;i++) cout<<t[i]<<" ";
// cout<<endl;
sort(t,t+n);
int m=unique(t,t+n)-t;
// cout<<m<<endl;
for(int i=0;i<n;i++) a[i]=lower_bound(t,t+m,a[i])-t+1;
// for(int i=0;i<n;i++) cout<<a[i]<<" ";
}
这和上一个程序是差不多的,但是这个是从0开始存储,与上一个程序略有不同,在引用lower_bound时我们加了1,原因就是如果我们不加1的话,原本离散化后的数值都会比正常少1。
这里读者可以试一试,毕竟输出语句已经帮你写好了。
第二种
这种在复杂度上比第一种更优,但是不能处理有重复数据的情况。
代码
struct node{
int ai,num;
bool operator < (node q)
{
return ai<q.ai;
}
};
node a[N];
int n,rank[N];
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i].ai,a[i].num=i;
// for(int i=1;i<=n;i++) cout<<a[i].ai<<" ";
sort(a+1,a+n+1);
for(int i=1;i<=n;i++) rank[a[i].num]=i;
// cout<<endl;
// for(int i=1;i<=n;i++) cout<<rank[i]<<" ";
}
在结构体里重载了小于号,目的是排序,主要语句就是rank[a[i].num]=i这个语句,由于a已经排好序了,所以a[i]是按照关键字ai从小到大;排列的在num里存储的是该数在原来数组里的位置,i及时这个数的对数,这个程序比较好理解。
转载请注明。邮箱1215962534@qq.com