有一个整形数组A,请设计一个复杂度为O(n)的算法,算出排序后相邻两数的最大差值。
给定一个int数组A和A的大小n,请返回最大的差值。保证数组元素多于1个。
测试样例:
[1,2,5,4,6],5
返回:2
解析:使用bucket排序思想,把大小为n的数组分成(max-min)/n个区间,显然这个区间是1 ~ n。
映射函数如下:
((num - min) * n / (max - min));
这样写是因为(num-min) / ((max-min) / n) = ((num - min) * n / (max - min));
比如num=6,min=1,max=6,(6-1) * 5 / (6-1) = 5,所以6在5号桶。这个映射函数决定了桶序号不可能为0,所以编程时要先剔除为0的空桶。
由于在任何一个桶中任何两个数的差值都不可能大于区间差值,而在空桶左右两边不空的桶里,相邻数的差值肯定大于区间值。所以产生最大差值的两个相邻数肯定来自不同的桶。所以只要计算桶之间数的间距就可以,也就是只用记录每个桶的最大值和最小值,最大差值可能来自某个非空桶的最小值减去前一个非空桶的最大值。
代码如下:
class Gap {
public:
int maxGap(vector<int> A, int n) {
if(n < 0 || ((A.size()==0) ^ (n==0)))
return -1;
int res = 0;
if(n < 2)
return res;
int max = A[0], min = A[0];
for(int i=1; i<n; ++i){
if(A[i] > max)
max = A[i];
if(A[i] < min)
min = A[i];
}
if(max == min)
return res;
vector<bool> has_num(n+1, false); //用来标记是否是非空桶
vector<int> maxs(n+1, 0);
vector<int> mins(n+1, 0);
for(int i=0; i<n; ++i){
int bucket_id = get_bucket_id(A[i], n, min, max);
maxs[bucket_id] = has_num[bucket_id] ? std::max(maxs[bucket_id], A[i]) : A[i];
mins[bucket_id] = has_num[bucket_id] ? std::min(mins[bucket_id], A[i]) : A[i];
has_num[bucket_id] = true;
}
int k = 0;
int last_max = 0;
while(k <= n){
if(has_num[k]){
last_max = maxs[k]; //排除序号为0的空桶,因为前面都是空桶,不是数据,相邻最大差是数据与数据之间的最大差,所以第一个last_max=maxs[k],
break; //从下一个mins减去这个last_max开始找出最大的差
}
++k;
}
while(++k <= n){
if(has_num[k]){ //只有非空桶才可以进入,所以res一直为两个非空桶的差
res = std::max(mins[k]-last_max, res);
last_max = maxs[k]; //注意更新last_max
}
}
return res;
}
private:
int get_bucket_id(long long num, long long n, long long min, long long max){ //使用long long类型防止溢出
return (int)((num - min) * n / (max - min));
}
};
整段程序的核心就是:
while(++k <= n){
if(has_num[k]){ //只有非空桶才可以进入,所以res一直为两个非空桶的差
res = std::max(mins[k]-last_max, res);
last_max = maxs[k]; //注意更新last_max
}
}
这一段程序通过++k保证了在之前k的基础上加1,has_num[k]保证只有非空桶进入该条件,所以相邻两个非空桶就会进入,并计算res。我们要注意更新last_max。