1、何为离散化?
离散化:把无限空间中有限的个体映射到有限的空间中去,即使离散的点(差距很大的数值)转换成更加紧密的点,以此提高算法的时空效率。当我们只关心数据的大小关系时,用排名顺序代替原数据进行预处理方法。离散化本质上是一种哈希,它在保持原序列大小关系的前提下把其映射成正整数。
2、离散化适用条件
原数据很大或含有负数、小数时,难以表示为数组下标,一些算法和数据结构(如BIT)无法运作,这时我们就可以考虑将其离散化。
3、离散化操作
一般离散化操作是将值域非常大的有限个数,映射到另外的数组中,每个值将与数组的下标具有映射关系,一一对应!
在此过程中,通常要执行的操作是
1、对需要映射或者需要进行操作的值的大小进行排序
2、需要映射或者需要进行操作的值中可能存在重复的,因此需要对涉及的重复的值进行去重操作
如图所示,就是一个离散化的简单例子,我们可以通过用代码实现该离散化的算法模板,代码如下:
#include <vector>
#include <algorithm>
using namespace std;
vector<int> a;//用于存储待离散化或待操作的值
sort(a.begin(),a.end());//将待离散化的所有值进行排序
a.erase(unique(a.begin(),a.end()),a.end())//对待离散化的所有值进行去重
a.erase(): 这是vector容器的一个成员函数,用于从容器中删除指定位置或范围的元素。
unique() 的返回值是一个迭代器,指向容器中不重复部分的下一个位置。
4、映射后如何找的某个值x对应离散化后的数组下标
如图所示,例如需要查找值为9对应离散化后的数组下标,则采用二分可以查找到满足某条件下区间的边界点的特性以及离散化数组中已经去重的特性,从而查找到值为9对应的唯一坐标。代码如下:
//找的某个值x对应离散化后的数组下标
int find(int x){
int l=0,r=a.size()-1;
while(r<l){
int mid=l+r>>1;
if(a[mid]>=x) r=mid;
else l=mid+1;
}
return r;
}
5、离散化例题
AC代码如下:
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int N = 300010;//n次加c操作,m次l,r的查询操作涉及个数可能3*10e5
vector<int> a;//用于存储待离散化或待操作的值
int b[N];//用于记录修改值的对应操作
int s[N];//前缀和数组
//输入修改操作和查询操作(使用键值对记录)
vector<pair<int, int>> add, query;
//二分查找值离散化后的位置
int find(int x) {
int l = 0, r = a.size() - 1;
while (l < r) {
int mid = l + r >> 1;
if (a[mid] >= x) r = mid;
else l = mid + 1;
}
return l;
}
int main() {
int n, m;
cin >> n >> m;
for (int i = 0; i < n; i++) {
int x, c;
cin >> x >> c;
//将修改操作的键值对加入到add数组里
add.push_back({ x,c });
//离散化第一步
a.push_back(x);
}
for (int i = 0; i < m; i++) {
int l, r;
cin >> l >> r;
//将查询操作的键值对加入到add数组里
query.push_back({ l,r });
//离散化第一步
a.push_back(l);
a.push_back(r);
}
sort(a.begin(),a.end());//排序
a.erase(unique(a.begin(), a.end()), a.end());//去重
//执行修改操作
for (auto item : add) {
int j = find(item.first);
b[j] += item.second;
}
//m次查询 m<=10e5,次数用前缀和,优化时间复杂度
//求前缀和数组
for (int i = 0; i < a.size(); i++) {
if(i==0) s[0] = b[0];
else s[i] = s[i-1] + b[i];
}
//执行m次询问操作
for (auto item : query) {
int i = find(item.first);
int j = find(item.second);
//此处写为cout<<s[j+1] - s[i]<<endl,是错误的,
//因为通过find函数查询到的离散化坐标就是对应的b中数组坐标
//建议与前缀和相关的算法,均设置为从数组下标1开始
if (i == 0) cout<<s[j]<<endl;
else cout<<s[j] - s[i-1]<<endl;
}
return 0;
}