程序设计之离散化(~详细整理解析)

程序设计之离散化(~详细整理解析)

概述

离散化是程序设计中常用到的一个技巧,它可以有效的降低时间复杂度。其基本思想就是在众多可能的情况中,只考虑需要用的值。离散化可以改进一个低效的算法,甚至实现根本不可能实现的算法。要掌握这个思想,必须从大量的题目中理解此方法的特点。例如,在建造线段树空间不够的情况下,可以考虑离散化。

那么离散化究竟是什么?就是把无限空间中的有限个体映射到有限空间中去,以此来提高时空效率。我们举个例子:对于一个有限序列,它的值域却非常大,可我们只需要在意它们之间的相对大小关系,那么则可以对数据进行适当的缩小,前提是不改变相对大小。比如:原数据是:1,999,100000,15。那么我们经过离散化就可以变为1,3,4,2。原数据:{100,200},{20,50000},{1,400};处理后:{3,4},{2,6},{1,5};我们发现经过离散化之后数据之间的相对大小是没有变化的。

那么我们什么时候该进行离散化呢?

有些数据本身很大, 自身无法作为数组的下标保存对应的属性。如果这时只是需要这堆数据的相对属性, 那么可以对其进行离散化处理。当数据只与它们之间的相对大小有关,而与具体是多少无关时,可以进行离散化。
例如:
91054与52143的逆序对个数相同。
设有4个数:
1234567、123456789、12345678、123456
排序:123456<1234567<12345678<123456789
=>1<2<3<4
那么这4个数可以表示成:2、4、3、1。

离散化的两种实现

PS:第一种方法相同的元素离散后的数值会不同,这是弊端,故若序列中存在这样的相同元素,我们应该采用第二种方法。

  • 利用结构体数组存储信息离散化

    我们可以这样想,在原序列中我们并不在乎它们的数据关系,我们只考虑它们的大小关系和相对位置,所以我们可以利用一个结构体数组存储它们的数据和位置,再根据它们的数据大小排序,新开一个序列存储它们的相对大小关系
    具体实现如下:

/*
*邮箱:[email protected]
*blog:https://me.csdn.net/hzf0701
*注:文章若有任何问题请私信我或评论区留言,谢谢支持。
*
*/
#include<bits/stdc++.h>	//POJ不支持

#define rep(i,a,n) for (int i=a;i<=n;i++)//i为循环变量,a为初始值,n为界限值,递增
#define per(i,a,n) for (int i=a;i>=n;i--)//i为循环变量, a为初始值,n为界限值,递减。
#define pb push_back
#define IOS ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
#define fi first
#define se second
#define mp make_pair

using namespace std;

const int inf = 0x3f3f3f3f;//无穷大
const int maxn = 1e5;//最大值。
typedef long long ll;
typedef long double ld;
typedef pair<ll, ll>  pll;
typedef pair<int, int> pii;
//*******************************分割线,以上为自定义代码模板***************************************//

struct node{
	int num;//数据大小
	int id;//原序列位置编号
	bool operator <(const node&a) {
		//运算法重载,用于我们的排序,获取相对大小关系。
		return num<a.num;
	}
};
node nums[maxn];//原序列。
int res[maxn];//离散化后的序列。
int main(){
	//freopen("in.txt", "r", stdin);//提交的时候要注释掉
	//IOS;
	int n;
	cout<<"请输入序列大小:";
	cin>>n;
	cout<<"请输入序列元素"<<endl;
	rep(i,1,n){
		//我们这里编号从1开始,切记。
		cin>>nums[i].num;
		nums[i].id=i;//获取位置编号
	}
	sort(nums+1,nums+n+1);//注意这里的写法,区间要注意。
	rep(i,1,n){
		//得到离散化后的序列。
		res[nums[i].id]=i;
	}
	cout<<"离散化后的序列"<<endl;
	rep(i,1,n){
		cout<<res[i]<<" ";
	}
	cout<<endl;
	return 0;
}

测试结果:
在这里插入图片描述

  • 使用STL算法离散化
    我们的思路是这样:思路是:先排序,再删除重复元素,最后就是索引元素离散化后对应的值。排序利用sort,删除利用unique,这样我们可以利用返回值计算不重复元素的个数,然后最后利用low_bound获取索引元素离散化后对应的值。这种方法就是可以用于相同元素的序列。

代码实现:

/*
*邮箱:[email protected]
*blog:https://me.csdn.net/hzf0701
*注:文章若有任何问题请私信我或评论区留言,谢谢支持。
*
*/
#include<bits/stdc++.h>	//POJ不支持

#define rep(i,a,n) for (int i=a;i<=n;i++)//i为循环变量,a为初始值,n为界限值,递增
#define per(i,a,n) for (int i=a;i>=n;i--)//i为循环变量, a为初始值,n为界限值,递减。
#define pb push_back
#define IOS ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
#define fi first
#define se second
#define mp make_pair

using namespace std;

const int inf = 0x3f3f3f3f;//无穷大
const int maxn = 1e5;//最大值。
typedef long long ll;
typedef long double ld;
typedef pair<ll, ll>  pll;
typedef pair<int, int> pii;
//*******************************分割线,以上为自定义代码模板***************************************//

int nums[maxn];//原序列。
int res[maxn];//离散化后的序列。
int main(){
	//freopen("in.txt", "r", stdin);//提交的时候要注释掉
	//IOS;
	int n;
	cout<<"请输入序列大小:";
	cin>>n;
	cout<<"请输入序列元素"<<endl;
	rep(i,1,n){
		//我们这里编号从1开始,切记。
		cin>>nums[i];
		res[i]=nums[i];
	}
	sort(nums+1,nums+n+1);//注意这里的写法,区间要注意。
	int cnt=unique(nums+1,nums+n+1)-(nums+1);//获取元素个数。
	rep(i,1,n){
		//得到离散化后的序列。
		res[i]=lower_bound(nums+1,nums+cnt+1,res[i])-nums;
	}
	cout<<"离散化后的序列"<<endl;
	rep(i,1,n){
		cout<<res[i]<<" ";
	}
	cout<<endl;
	return 0;
}

测试结果:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/hzf0701/article/details/107891703