原来我所介绍的排序都是基于比较的排序,例如快排、堆、归并等等,而桶排序不是基于比较的排序,而是一种基于的是数据状况的排序,桶排序其实是已知要排序的数据所在的区间,把该区间均匀分成n个桶,这n个桶包含要排序的数的所有结果,我们把相应的结果放到相应的桶里,然后再按次序把每个桶里面的数据倒到一个数组里,这个数据就有序了。很明显,他的时间复杂度是O(n)和空间复杂度是O(n),因为桶排序的桶(就是一种容器)可以使任意的数据结构来完成,所以其可以做呈稳定的比如把桶做成队列这种结构,桶排序就是一种概念,实现这种感念的方式有两种,一种叫做基数排序,一种叫做计数排序。
其实桶排序是一种思想,简单桶排序也就是计数排序,下面来看桶排序的简单实现:
public class BucketSort {
public static void bucketSort(int[] a){
//一样是先判断是否要进行排序
if(a == null && a.length<2){
return;
}
//最大值先初始化为最小值
int max = Integer.MIN_VALUE;
//拿到这组数据的最大值
for(int i = 0;i < a.length; i++){
max = Math.max(max, a[i]);
}
//桶的数量是这组数据最大值+1为得就是能装下该范围的所有数据
int[] bucket = new int[max + 1];
//要是数据在这个桶里就把这个桶进行+1操作表示这个桶里装了多少相同的数据
for(int i = 0; i < a.length; i++){
bucket[a[i]]++;
}
//然后把每个桶里的数据按桶从小到大倒出来就欧卡了
int i = 0;
for(int j = 0; j < bucket.length; j++){
while(bucket[j]-- > 0){
a[i++] = j;
}
}
}
public static void main(String[] args) {
//初始化存储数据
int [] table = new int[10];
Random random = new Random();
//随机生成100个100以内的整数,并存入数组
for(int i=0;i<10;i++){
table[i] = random.nextInt(100);
}
//遍历初始化数组
System.out.print("原数组序列是:");
for(int j:table){
System.out.print(j+" ");
}
//堆排序
bucketSort(table);
//输出经过堆排序之后的数组
System.out.println();
System.out.print("经过桶排序的到的序列是:");
for(int j:table){
System.out.print(j+" ");
}
}
}
运行结果:
下面来看一种基于桶排序思想的一道题:
就是给定一个数组,这个数组的数据的每个数据都巨long(其实也就是不让你使用排序算法去解决这道问题),让你求排序之后相邻的两个数据的最大差值。
emmm,刚看到这道题,很难受,让求排序之后的相邻数据的最大差值,还不让你排序,有点变态,但是因为不让排序,但给定了一组数据,其实也就是知道了这组数据所处的状态,也就是最值,长度等等,根据一组数据所处状态然后再去分析,我们考虑到了可以使用桶排序的思想(但不是桶排序),我们可以拿到这组数据的最大值和最小值,其实也就是拿到它的区间,也能拿到这组数据的个数(数组长度假设是n),所以我们根据桶排序的思想把这组数据分成n+1组,每组数据之间的差值其实是有限的,也就是这组数据的长度-1,但因为我们有n+1个桶,所以我们的桶必然就会有一个是空桶,一定有不相邻的两个非空桶,他们两组数据之间的差值一定大于桶内数据的差值,所以,这个方法其实只需要把每个非空桶的最大值和最小值求出来,所有非空桶相邻后面的最小值减去前面的最大值记为所求解之一,所求解的最大值即为所求。下面来看代码:
public class T1 {
public static int maxGap(int[] a){
if(a == null || a.length<2){
return 0;
}
//求这组数据的状态,也就是长度,最大最小值
int len = a.length;
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for(int i = 0; i < len; i++){
min = Math.min(min, a[i]);
max = Math.max(max, a[i]);
}
//如果最大值等于最小值说明等于0
if(min == max){
return 0;
}
//用三个数组来表示每个桶的有效状态,包括是否为空桶、最大最小值
boolean[] hasNum = new boolean[len + 1];
int[] mins = new int[len + 1];
int[] maxs = new int[len + 1];
int bid = 0;
for(int i = 0;i<len;i++){
//根据我们传进来的元素,长度,最小值,最大值来去判断我这个元素该装哪个桶里去
bid = bucket(a[i],len,min,max);
//把每个桶的最大值和最小值记录下来
mins[bid] = hasNum[bid]?Math.min(mins[bid], a[i]):a[i];
maxs[bid] = hasNum[bid]?Math.max(maxs[bid], a[i]):a[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;
}
//这个方法就是用来判断这个数据到底该放在哪个桶里
//其实就是这个数据在这个单位长度里(len/max-min就是指的是单位长度,也就是现在的数表示的就是桶号了),
//i-min就是在这个桶序号
public static int bucket(long i, long len, long min, long max) {
return (int )((i-min)*len/(max-min));
}
public static void main(String[] args) {
//初始化存储数据
int [] table = new int[10];
Random random = new Random();
//随机生成10个100以内的整数,并存入数组
for(int i=0;i<10;i++){
table[i] = random.nextInt(100);
}
//遍历初始化数组
System.out.print("原数组序列是:");
for(int j:table){
System.out.print(j+" ");
}
System.out.print("最大差值是是:"+maxGap(table));
}
}
运行结果: