题目:有一个整形数组A,请设计一个复杂度为O(n)的算法,算出排序后相邻两数的最大差值。给定一个int数组A和A的大小n,请返回最大的差值。保证数组元素多于1个。测试样例:[1,2,5,4,6],5返回:2
思路:直接的思路,先排序,再遍历数组求出相邻2个数的差,保留最大值即可,此时时间复杂度为O(nlogn)。本题采用的是桶排序的思想,对于n个数,设置n+1个桶,具体的,先遍历数组找出min和max,差值区间设置为d=(max-min)/n,于是区间是[min~min+d);[min+d~min+2d)……[min+(n-1)d~min+nd)这n区间,再单独为最大值max设置一个桶,于是得到n+1个桶,已知数组元素总共有n个,但是桶有n+1个,并且元素min必定在第一个桶中,max必定在n+1号桶中;显然要把n个元素放入到n+1个桶中必定会产生空桶,已知数组中的元素时整数,而虽然每个桶区间不一定是整数,例如[23/7,31/7),但是没有关系,对于任意一个元素,它总会落入到某一个桶中,并且可以知道,不同桶之间元素的差值必定大于d,同一个桶中的元素的差值必定小于d,于是可知要求元素之间的最大差值只需要在不同的桶之间寻找,不用在同一个桶中寻找,并且题目要求是排序后相邻2个元素的最大差值,于是应该寻找2个相邻非空桶中前一个桶的最大值与后一个桶的最小值之间的差值,遍历所有桶,比较得到所有差值中的最大值就是结果所要求的相邻2数最大差值。注意理解:最终要对每2个非空的相邻的桶求差值,而不能根据空桶的数目来确定2个相邻桶的差值,因为桶的长度可能是小数,那么间隔2个空桶和间隔3个空桶并不意味着后者具有更大的差值,因此应该对所有相邻的非空桶根据前桶的最大值和后桶的最小值求出差值作为比较对象,因此在遍历时对于每一个桶,要求出这个桶中元素的最大值maxOfBucket[i],最小值minOfBucket[i],并且保留上一个非空桶的maxOfLastBucket,于是差值着2个相邻非空桶的差值就是result= minOfBucket[i]- maxOfLastBucket;然后将当前桶的maxOfBucket[i]最为下一个桶的maxOfLastBucket。逐步替换result,当遍历完成时就可以得到最大差值。
具体的解决方式:
①已知应该设置的桶的数目是n+1,建立一个数组boolean[] hasNumber=new blloean[n+1];用来表示每个桶i中是否存在元素;如果没有元素就是空桶,可以直接跳过。
②设置数组maxOfBucket[],minOfBucket[]用来记录每个桶中元素的最大值和最小值,设置变量maxOfLastBucket用来表示上一个非空桶的最大值。
③创建方法private int getBucketIndex(int number,intlength,int max,int min)根据一个数组的长度,最大值最小值,已知数据number,计算number这个数会被散列到的桶的下标。
④先将数组中的所有元素散列到桶中,更新所在桶的hasNumber[i]、maxOfBucket[i]、minOfBucket[i]的值;
然后从i=0的桶开始遍历桶,先找到第一个非空的桶,将其最大值作为下一个元素的maxOfLastBucket,然后往下遍历,跳过空的桶,遇到非空的桶,就计算result= minOfBucket[i]- maxOfLastBucket,并且与已有的result比较,保留最大值,直到遍历结束返回结果result即可。
importjava.util.*;
//这道题目的思路很棒,巧妙而灵活:利用桶排序的思想散列数组,求最大差值就是求2个相邻非空桶的最大差值
publicclass Gap {
public int maxGap(int[] A, int n) {
//特殊输入:如果只有一个元素,显然最大差值为0
if(A==null||A.length<2) return 0;
//创建n+1个桶,hasNumber[]表示该桶是否有元素
boolean[] hasNumber=newboolean[A.length+1];
//每个桶中可以有多个数,记录每个桶的最大值和最小值
int[] maxOfBucket=new int[A.length+1];
int[] minOfBucket=new int[A.length+1];
//记录上一个桶的最大值
int maxOfLastBucket=0;
//进行散列,把数组中的元素散入桶,注意不要搞混桶的下标和元素的下标,这里研究的都是桶的下标
for(int i=0;i<A.length;i++){
//求出元素A[i]落入的桶的下标bucketIndex
intbucketIndex=this.getIndexOfBucket(A,i);
//判断桶中是否已经有元素
if(hasNumber[bucketIndex]){
//如果存在,则要比较A[i]与已有的maxOfBucket[i]和minOfBucket[i]
maxOfBucket[bucketIndex]=A[i]>maxOfBucket[bucketIndex]?A[i]:maxOfBucket[bucketIndex];
minOfBucket[bucketIndex]=A[i]<minOfBucket[bucketIndex]?A[i]:minOfBucket[bucketIndex];
}else{
//若不存在则将A[i]加入并作为maxOfBucket[i]和minOfBucket[i]
maxOfBucket[bucketIndex]=A[i];
minOfBucket[bucketIndex]=A[i];
//将hasNumber[i]设为true表示桶中有元素,boolean[]数组值默认为false
hasNumber[bucketIndex]=true;
}
}
//上面已经将所有元素散列到了n+1个桶中,开始遍历桶求非空相邻桶之间的最大差值
//先找到第一个非空的桶,显然第一个桶bucketIndex=0就是非空的(存放最小值),将其最大元素值作为maxOfLastBucket
maxOfLastBucket=maxOfBucket[0];
//result就是结果最大差值,初始值设为0,当全部元素相同时就取到0
int result=0;
//从第二个桶开始遍历,跳过空桶,共有n+1个桶,编号从0~n求出2个相邻非空桶的最大差值
for(int i=1;i<n+1;i++){
//跳过空的桶,即不做任何处理
if(hasNumber[i]){
//不为空桶
//比较是否替换最大差值
result=(minOfBucket[i]-maxOfLastBucket)>result? (minOfBucket[i]-maxOfLastBucket):result;
//将当前桶的最大值作为下一个桶的maxOfLastBucket
maxOfLastBucket=maxOfBucket[i];
}
}
return result;
}
//已知一个数组A,并且已知将其分成n+1个桶,给定A[i],求出应该落入的桶的下标
private int getIndexOfBucket(int[]array,int i){
//求出数组array的最大、最小值
int max=array[0];
int min=array[0];
for(int j=1;j<array.length;j++){
max=array[j]>max? array[j]:max;
min=array[j]<min? array[j]:min;
}
//这是一个逻辑,用于根据A[i]求出落入桶的下标
intbucketIndex=(int)(((double)(array[i]-min))/(max-min)*array.length);
return bucketIndex;
}
}