浅谈离散化
离散化
前言:
因为做洛谷P1955 程序自动分析 的时候,卡在离散化这里,硬是过不了第二个点
所以...感觉离散化还是逃不掉,得学...
于是...我就去看了资料看了博客,现在写一下学习记录
知识搬运:
我们还是先通过问答的形式来了解一下“离散化”
- 离散化是什么啊?在什么地方用啊?
面对数据贼大的情况(例如上面那道题,1e9的数据),直接开数组肯定会炸,所以我们引入“离散化”这个东西,将大数据映射到一个较小的值域来处理
- 那怎么实现离散化呢?
主要有两种吧(也许更多,可以自行学习):
- STL实现离散化(STL大法好啊 orz)
STL实现离散化呢,还是很简单的,就是如下三步:
(1)排序(sort函数)
(2)去重(unique函数):返回去重之后的尾迭代器(或指针),即去重之后末尾元素的下一个位置
把一个vector去重:
int m=unique(a.begin(),a.end())-a.begin();
把一个数组去重,元素存放在下标1~n:
int m=unique(a+1,a+1+n)-(a+1);
\(unique\)函数也可以手打:
void discrete() {
sort(a+1,a+1+n);
for(int i=1;i<=n;i++) {
if(i==1||a[i]!=a[i-1]) {
b[++m]=a[i];
}
}
}
(3)二分索引(lower_bound或upper_bound函数):两个迭代器(或指针)指定的部分应该是提前排好序的
lower_bound的第三个参数传入一个元素x,在两个迭代器(或指针)指定的部分上执行二分查找,返回第一个大于等于x的元素的位置的迭代器(或指针)
upper_bound的用法和lower_bound大致相同,唯一的区别是查找第一个大于x的元素
在有序int数组,元素下标1~n,查找大于等于x的最小整数的下标:
int i=lower_bound(a+1,a+1+n,x)-a;
在一个有序的vector<int>中查找小于等于x的最大整数:
int y=*--upper_bound(a.begin(),a.end(),x);
不好理解?我们来举个栗子:
n=7
a[](b[]):4 3 6 10 7 7 8
sort之后:3 4 6 7 7 8 10
unique后:3 4 6 7 8 10
此时unique返回的的迭代器(或指针)指向元素10的后一个位置,(a+1)表示元素3的位置
那么unique(a+1,a+1+n)-(a+1)=7-1=6,即为去重后的元素个数
现给出完整STL实现离散化的代码,如下:
#include <bits/stdc++.h>
using namespace sts;
int n,res,tot,a[MAXN],b[MAXN];
int main() {
scanf("%d",&n);
for(register int i=1;i<=n;i++) {
scanf("%d",&a[i]);
b[++tot]=a[i]; //b[]是a[]的副本
}
sort(b+1,b+1+tot); //第一步:排序
res=unique(b+1,b+1+tot)-(b+1); //unique函数计算出去重后的元素个数,存在res中
for(register int i=1;i<=n;i++) {
a[i]=lower_bound(b+1,b+1+res,a[i])-b; //lower_bound函数进行索引
}
return 0;
}
- Hash表(也叫散列表)实现离散化
Hash表一般有Hash函数(散列函数)与链表结构共同实现,通过Hash函数将原值映射到一个容易维护的值域中
但是使用Hash肯定避不开的就是Hash冲突,解决方案是建立一个邻接表结构,以Hash函数的值域作为head,映射后值相同的原始信息被分到同一个head后面,构成链表
这里给出一篇使用Hash表实现离散化题解,感觉讲得还蛮详细
补充
这里补充一种比较低配的实现离散化的方式:map函数
我们知道,map函数实现的就是两种相同或不同数据类型之间的映射
那我们就可以使用map实现离散化啊!代码如下:
#include <bits/stdc++.h>
using namespace std;
int n,u,v,tot;
map<int,int> shan;
int main() {
scanf("%d",&n);
for(register int i=1;i<=n;i++) {
scanf("%d%d",&u,&v);
if(shan[u]==0) shan[u]=++tot;
if(shan[v]==0) shan[v]=++tot;
}
return 0;
}