算法基础——离散化

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;

}
  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值