1.非基于比较的排序,与被排序的样本的实际数据状况很有关系,所以实际中并不经常使用
2.时间复杂度O(N),额外空间复杂度O(N)
3.稳定的排序
题目一:给定一个数组,如果排序之后,相邻两数的最大差值,要求时间复杂度O(N),且要求不能用非基于比较的排序
分析:借用了桶的概念,但没有进行桶排序。
步骤:如果一个数组中有N个数,就准备N+1个桶(0,1,2,...N),每个桶只记录单独一个桶当一个数进到这个桶的时候,我只更新这个桶出现的所有数的最小值和最大值。只留下两个信息,所有进过这个桶的最小值和所有进过这个桶的最大值,还要设置一个bool类型的值:这个桶进没进来过数。假设遇到一个数x,假设x归属到了7号桶,则7号桶的bool从false改为true。如果x是第一次第一个进到这个桶的数,它的最小值就是x,最大值也是x,继续遍历,(每个桶都是这样操作),假设y也进到了7号桶,此时只去更新7号桶的最小值和最大值,如果y<x,那么7号桶的最小值从x变成y;如果y>x,那么7号桶的最大值从x变成y;假设z也进到了7号桶,也只去更新这个桶的最小值或最大值,多余的数一概不留不存。当整个数组遍历完成之后,每个桶就有了相应的信息(最小值,最大值,是否进过数),然后从1号桶开始,如果它是空的直接跳下一个桶,如果它是非空的,找前一个非空桶,也就是对于后面任何一个非空桶 都找左边离它最近的非空桶,它左边离它最近非空桶的最大值和它自己的最小值算一下差值,我们要求的最大差值一定在这些值中,用一个全局变量就可以找到。
具体做法:先遍历整个数组找到最小值min,和最大值max,如果最小值和最大值相等,返回0;
如果最小值和最大值不等,最小值放进0号桶,最大值放在第N号桶,把最小值min和最大值max这个区间等分成N+1份,一个数属于哪个区间,就放进哪个桶里。
设置一个空桶的目的是保证两个数的最大差值一定不来自相同桶的数。原因是空桶左边肯定存在一个离它最近的不空的桶,空桶右边也有一个离它最近的不空的桶,左边非空桶收集到的最大值和右边非空桶收集到的在排序之后一定是相邻的,而这个差值一定会大于每个桶表示的范围,而来自同一个桶内部两个相邻数产生的差值一定小于桶所表达的范围。这样设计的目的就是我们可以不去管来自一个桶内部相邻数字产生的差值,因为我们要找的是最大差值,这个最大差值只会来自两个数来自不同桶产生的差值。
为什么不直接找某个空桶然后看这个空桶左侧非空桶和右侧非空桶的最大差值?
因为设置一个空桶的目的是保证两个数的最大差值一定不来自一个桶内部,即一个桶内部的数的差值一定不是我们想要的答案,但是并不能保证答案一定来自空桶两侧非空桶。
import java.util.Arrays;
public class MaxGap {
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) {
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]) {
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));
}
// for test
public static int comparator(int[] nums) {
if (nums == null || nums.length < 2) {
return 0;
}
Arrays.sort(nums);
int gap = Integer.MIN_VALUE;
for (int i = 1; i < nums.length; i++) {
gap = Math.max(nums[i] - nums[i - 1], gap);
}
return gap;
}
// for test
public static int[] generateRandomArray(int maxSize, int maxValue) {
int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
}
return arr;
}
// for test
public static int[] copyArray(int[] arr) {
if (arr == null) {
return null;
}
int[] res = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
res[i] = arr[i];
}
return res;
}
// for test
public static void main(String[] args) {
int testTime = 500000;
int maxSize = 100;
int maxValue = 100;
boolean succeed = true;
for (int i = 0; i < testTime; i++) {
int[] arr1 = generateRandomArray(maxSize, maxValue);
int[] arr2 = copyArray(arr1);
if (maxGap(arr1) != comparator(arr2)) {
succeed = false;
break;
}
}
System.out.println(succeed ? "Nice!" : "Fucking fucked!");
}
}