离散化是程序设计中常用到的一个技巧,它可以有效的降低时间复杂度。其基本思想就是在众多可能的情况中,只考虑需要用的值。离散化可以改进一个低效的算法,甚至实现根本不可能实现的算法。要掌握这个思想,必须从大量的题目中理解此方法的特点。例如,在建造线段树空间不够的情况下,可以考虑离散化。
那么离散化究竟是什么?就是把无限空间中的有限个体映射到有限空间中去,以此来提高时空效率。我们举个例子:对于一个有限序列,它的值域却非常大,可我们只需要在意它们之间的相对大小关系,那么则可以对数据进行适当的缩小,前提是不改变相对大小。比如:原数据是: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 //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;
}
代码实现:
/*
*邮箱:[email protected]
*blog:https://me.csdn.net/hzf0701
*注:文章若有任何问题请私信我或评论区留言,谢谢支持。
*
*/
#include //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;
}