桶排序的思路:
首先找到数组a中的最大值;然后建立一个长度为最大值加1的数组;遍历a数组,如果a[i]的值为b,那么新的数组中下标为b的位置的值加1。
比如:a数组是【1,2,2,4,3,2】,那么新的数组为【0,1,3,1,1】
分析:新数组中o位置为0,说明a数组中没有0;1位置为1,说明a数组中有1个1;2位置为3,说明a数组有3个2;3位置为1,说明a数组中有1个3;4位置为1,说明a数组有1个4。
时间复杂度分析:
对原数组进行一次遍历(找到最大值),时间复杂度是O(n);
对原数组进行第二次遍历(进行装桶操作),时间复杂度是O(n);
排好序有序输出打印,遍历所有的桶,时间复杂度是O(m);
其中n表示数据样本的长度,m表示桶的个数;
桶排序的时间复杂度是O(n),额外的空间复杂度是O(n)。
代码实现:
public static void bucketSort(int[] arr){
if(arr==null||arr.length<2){
return;
}
//1.获取数组的最大值(遍历一次原数组)
int max=Integer_Min_Value;
for(int i=0;i<arr.length;i++){
max=Math.max(arr[i],max);
}
//2.创建最大值长度加1的桶数组
int[] bucket=new int[max+1];
//3.给桶中添加元素(遍历第二次原数组)
for(int i=0;i<arr.length;i++){
bucket[arr[i]]++;//下标等于原来数组中值的位置加1
}
//4.输出打印排好序的数据(遍历桶数组第一次)(将排好序的数据返回到原来的数组中)
int i=0;//表示原来数组的移动
for(int j=0;j<bucket.length;j++){
while(bucket[j]--!=0){//桶中的数字要不断变化用来控制原来数组要复制几次这个数
arr[i++]=j;
}
}
}
例题:
给定一个数组,求如果排序之后,相邻两数的最大差值,要求时间复杂度O(N),且要求不能用非基于比较的排序。
分析:要求是非基于比较的排序且时间复杂度是O(n),我们考虑使用堆排序。
1.首先遍历原数组,找到最大值和最小值。
2.创建一个辅助数组(桶的集合),长度为原数组长度加1,o位置放原数组的最小值,最后一个位置放原数组的最大值。
3.每个桶存放的要有三个数组:存在原数组中,并且被划分到了该范围内的最小值和最大值;一个布尔变量,用来表示该桶中有没有元素。
4.遍历原数组,填充桶中的数据。
5.空桶存在的意义表示的是相邻的差值最大的两个数不可能在同一个桶中,因为隔了一个空桶。比较非空桶中的最小值和它前一个非空桶的最大值的差值,非空桶中的最大值和它后一个非空桶中的最小值的差值,比较这两个数,取到一个最大值。
//找到相邻差值最大值的方法
public static int maxGap(int[] arr){
//1.考虑数组的特殊长度问题
if (arr==null||arr.length<2) {
return 0;
}
//2.遍历数组,找到数组中的最大值和最小值
int max=Integer.MIN_VALUE;
int min=Integer.MAX_VALUE;
int len=arr.length;
for (int i = 0; i < arr.length; i++) {
max=Math.max(max, arr[i]);
min=Math.min(min,arr[i]);
}
//3.考虑数组的特殊内容问题(只有一种数)
if(min==max){
return 0;
}
//4.建立好辅助的数组,桶中应该有最大值,最小值和是否有值,并且长度是原数组的长度加1
int[] maxs=new int[len+1];
int[] mins=new int[len+1];
boolean[] hasNum=new boolean[len+1];
//5.遍历数组,确定把数组中的每个数都填充到那个桶里面(bucket方法)(用bid来表示插入桶的坐标)
int bid=0;
for(int i=0;i<len;i++){
bid=bucket(arr[i],len,max,min);
mins[bid]=hasNum[bid]?Math.min(arr[i],mins[bid]):arr[i];
maxs[bid]=hasNum[bid]?Math.max(arr[i], maxs[bid]):arr[i];
hasNum[bid]=true;
}
//7.寻找差值最大的值:遍历辅助数组,
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;
}
//6.提供bucket方法,把数组中的数字划分到对应的桶中
public static int bucket(long num,long len,long max,long min) {
return (int)((num-min)*len/(max-min));
//每个桶的范围是(max-min)/len;要插入的数和最小值的间隔(num-min)包含几个桶的范围就放
//在几号桶中
}