问题:给定一个数组,求如果排序之后,相邻两数的最大差值,要求时
间复杂度O(N),且要求基于不能用非比较的排序。
排序前相邻两数的最大差值,一个for循环就能搞定
排序后相邻两数的最大差值,关键点:1.排序后 2.相邻两数 3.求最大差值就行,没要求输出排序结果
所以,可以利用桶的性质来求。
有N个数据的长度为N的数组,为它准备N+1个桶,那么至少有1个为空桶!(即便每个桶只装1个数也会多余1个桶出来)。
首先将数据最小值放入第一个桶0,将最大值放入最后一个桶N。其余数据进入对应桶((当前值 - 最小值) * N / (最大值 - 最小值));
之所以要设置空桶的目的就是为了保证同一个桶中的数,不可能存在最大差值。因为有空桶,空桶所占的长度就超过了桶内任意2数据的长度。而第一个桶和最后一个桶又肯定不是空桶,所以最大差值必然不存在与同一个桶内。但是这并不能证明空桶两边的数据就一定是最大差值:比如19,30,49
所以要求最大差值得在2个桶之间求。我们可以为每个桶设置3个属性:最大值,最小值,是否有数。
用当前非空桶的最小值减去前一个非空桶的最大值,遍历完整个数组求出整个数组的最大差值。
代码:
public static int maxGap(int[] nums) {
if (nums == null || nums.length < 2) {
return 0;
}
int len = nums.length;
int min = Integer.MAX_VALUE;//最小值初始化为一个极大值
int max = Integer.MIN_VALUE;//最大值初始化为一个极小值
for (int i = 0; i < len; i++) {//遍历数组求出最小值和最大值
min = Math.min(min, nums[i]);
max = Math.max(max, nums[i]);
}
if (min == max) {//数组只有1种数
return 0;
}
boolean[] hasNum = new boolean[len + 1];//对应每个桶是否有数
int[] maxs = new int[len + 1];//对应每个桶的最大值
int[] mins = new int[len + 1];//对应每个桶的最小值
int bid = 0;
for (int i = 0; i < len; i++) {
bid = bucket(nums[i], len, min, max);//判断该数,应该放入哪个桶
mins[bid] = hasNum[bid] ? Math.min(mins[bid], nums[i]) : nums[i];//若该桶有数,则判断该桶中的最小值与当前数谁更小,没数则把当前数给当前桶的最小值
maxs[bid] = hasNum[bid] ? Math.max(maxs[bid], nums[i]) : nums[i];//若该桶有数,则判断该桶中的最大值与当前数谁更大,没数则把当前数给当前桶的最大值
hasNum[bid] = true;//只要该桶进来了数据就修改该桶状态
}
int res = 0;
int lastMax = maxs[0];
int i = 1;
for (; i <= len; i++) {
if (hasNum[i]) {//求最大差值前提是当前桶有数存在,且最大差值必然在当前非空桶(mins[i])和前一个非空桶(因为不知道前一个桶是否为空,
//所以我们可以设置一个lastMax变量用来记录前一个非空桶的最大值,每经历一个非空桶就修改lastMax的值)之间(mins[i] - lastMax),
//求出来后与正真的最大差值外部变量res比较,大于就修改res,直到遍历结束。
res = Math.max(res, mins[i] - lastMax);
lastMax = maxs[i];
}
}
return res;
}
public static int bucket(long num, long len, long min, long max) {
return (int) ((num - min) * len / (max - min));
}