1 基本介绍
1.1 概述
桶排序 (Bucket sort)或所谓的箱排序,是一个排序算法,工作的原理是将数组分到有限数量的桶里。每个桶再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序),最后依次把各个桶中的记录列出来,即得到有序序列。桶排序是鸽巢排序的一种归纳结果。当要被排序的数组内的数值是均匀分配的时候,桶排序使用线性时间 o(n)。但桶排序并不是比较排序,它不受到O(n log n)下限的影响。
1.2 算法详解
桶排序的思想就是把待排序的数尽量均匀地放到各个桶中,再对各个桶进行局部的排序,最后再按序将各个桶中的数输出,即可得到排好序的数。
- 首先确定桶的个数。因为桶排序最好是将数据均匀地分散在各个桶中,那么桶的个数最好是应该根据数据的分散情况来确定。首先找出所有数据中的最大值mx和最小值mn;
- 根据mx和mn确定每个桶所装的数据的范围 size,有 size = (mx - mn) / n + 1,n为数据的个数,需要保证至少有一个桶,故而需要加个1;
- 求得了size即知道了每个桶所装数据的范围,还需要计算出所需的桶的个数cnt,有cnt = (mx - mn) / size + 1,需要保证每个桶至少要能装1个数,故而需要加个1;
- 求得了size和cnt后,即可知第一个桶装的数据范围为 [mn, mn + size),第二个桶为 [mn + size, mn + 2 *size),…,以此类推,因此步骤2中需要再扫描一遍数组,将待排序的各个数放进对应的桶中。
- 对各个桶中的数据进行排序,可以使用其他的排序算法排序,例如快速排序;也可以递归使用桶排序进行排序;
- 将各个桶中排好序的数据依次输出,最后得到的数据即为最终有序。
算法图解:
2 代码实现
/**
* 桶排序
*/
public class BucketSort {
//在链表中添加元素的同时需要进行元素的排序
public static void sort(ArrayList<Integer>list,int i) {
if(list==null)
list.add(i);
//这里采用的排序方式为插入排序
else {
int flag=list.size()-1;
while(flag>=0&&list.get(flag)>i) {
if(flag+1>=list.size())
list.add(list.get(flag));
else
list.set(flag+1, list.get(flag));
flag--;
}
if(flag != (list.size()-1))
//注意这里是flag+1,自己可以尝试将这里换成flag看看,会出现数组越界的情况
list.set(flag+1, i);
else
list.add(i);
}
}
public static void Bucketsort(int []num,int sum) {
//遍历得到数组中的最大值与最小值
int min=Integer.MAX_VALUE;
int max=Integer.MIN_VALUE;
for(int i=0;i<num.length;i++) {
min = min <= num[i] ? min: num[i];
max = max >= num[i] ? max: num[i];
}
//求出每个桶的长度,这里必须使用Double
double size=(double)(max-min+1)/sum;
ArrayList<Integer>list[]=new ArrayList[sum];
for(int i=0;i<sum;i++) {
list[i]=new ArrayList<Integer>();
}
//将每个元素放入对应的桶之中同时进行桶内元素的排序
for(int i=0;i<num.length;i++) {
System.out.println("元素:"+String.format("%-2s", num[i])+", 被分配到"+(int)Math.floor((num[i]-min)/size)+"号桶");
sort(list[(int)Math.floor((num[i]-min)/size)], num[i]);
}
System.out.println();
for(int i=0;i<sum;i++) {
System.out.println(String.format("%-1s", i)+"号桶内排序:"+list[i]);
}
System.out.println();
//顺序遍历各个桶,得出我们 已经排序号的序列
for(int i=0;i<list.length;i++) {
if(list[i]!=null){
for(int j=0;j<list[i].size();j++) {
System.out.print(list[i].get(j)+" ");
}
}
}
System.out.println();
System.out.println();
}
public static void main(String[] args) {
int []num ={7,4,56,31,9,3,32,2,1,45,8,54,54,6,5,10};
long startTime=System.currentTimeMillis();
//这里桶的数量可以你自己定义,这里我就定义成了3
Bucketsort(num, 3);
long endTime=System.currentTimeMillis();
System.out.println("程序运行时间: "+(endTime-startTime)+"ms");
}
}
3 复杂度分析
最好时间复杂度 : O(n + k)
其中k为桶的个数。即当数据是均匀分散排列的,那么每个桶分到的数据个数都是一样的,这个步骤需要O(k)的书剑复杂度,在对每个桶进行排序的时候,最好情况下是数据都已经是有序的了,那么最好的排序算法的时间复杂度会是O(n),因此总的时间复杂度是 O(n + k) 。
最坏时间复杂度:O(n^2)
当对每个桶中的数据进行排序的时候,所使用的排序算法,最坏情况下是O(n2)。
平均时间复杂度:O(n + n²/k + k) <=> O(n)
如果k是根据Θ(n)来获取的,那么平均时间复杂度就是 O(n)。
空间复杂度
上面我们已经说过了,桶排序本身也是一个通过空间来换取时间的算法,所以很明显他的空间复杂度就会很高.并且这个空间复杂度主要就取决于桶的数量以及桶的范围,所以假设有k个桶的话,那么空间复杂度就为O(n+k)