排序充斥着我们的生活,比如站队、排队买票、考试排名、公司业绩排名、将电子邮件按时间排序、QQ好友列表中的会员红名靠前,等等。
举个例子
这里先举个例子,通过这个例子让我们接触第一个算法。
在某个期末考试中,老师要把大家的分数排序,比如有5个学生,分别考5、9、5、1、6分(满分10分),从大到小排序应该是9、6、5、5、1,大家有没有办法写一段程序随机读取5个数,然后对他们排序呢?
看到这个问题,我们用5分钟想一下该怎么办。办法当然有很多,这里使用桶排序的思想来处理。
我们找到11个桶,分别编号为0-10,对应0-10分,如图3-1所示。
接着我们把这些分数按照桶的编号放入桶中,如图3-2所示。
接着我们从最大编号的桶到最小编号的桶依次输出每个桶中的分数,分别是9、6、5、5、1了。是不是很轻松地完成排序了呢?这就是桶排序的思想。
什么是桶排序
桶排序,也叫做箱排序,是一个排序算法,也是所有排序算法中最快、最简单的排序算法。其中的思想是我们首先需要知道所有待排序元素的范围,然后需要有在这个范围内的同样数量的桶,接着把元素放到对应的桶中,最后按顺序输出。
这实际上是简易版的桶排序,我们想象一下,如果考试分数的范围是0~100万该怎么办?弄100万个桶吗?
实际上在这种情况下,一个桶并不总是放同一个元素,在很多时候一个桶里可能会放多个元素,这是不是与散列表有点相似呢?其实真正的桶排序和散列表有一样的原理。
除了对一个桶内的元素做链表存储,我们也有可能对每个桶中的元素继续使用其他排序算法进行排序,所以更多时候,桶排序会结合其他排序算法一起使用。
桶排序的实现
我们怎么在代码中实现桶排序呢?其实很简单,使用数组就好了。比如有11个桶,我们只需要声明一个长度为11的数组,然后每把一个元素往桶中放时,就把数组指定位置的值加1,最终倒序输出数组的下标,数组每个位置的值为几就输出几次下标,这样就可以实现桶排序了。
下面我们一起看看桶排序的代码。
package BucketSort;
//桶排序
public class BucketSort {
private int[] buckets;
private int[] array;
public BucketSort(int range,int[] array) {
//传入一个范围,声明buckets[]数组的长度
//传入一个array数组
this.buckets = new int[range];
this.array = array;
}
/*int[] array = {5,9,1,9,5,3,7,6,1};
buckets[11];*/
/*排序*/
public void sort() {
//判断数组的长度大于1
if(array != null && array.length>1) {
for(int i = 0;i<array.length;i++) {
//如果传进来一个数,buckets[array[i]]会自增1,传进来两个或者更多相同的数,那么buckets[array[i]]会一直自增,直到下一个数据
buckets[array[i]] ++;
}
}
}
/*从大到小排序*/
public void print() {
//倒序输出数据
for (int i = buckets.length-1; i >= 0; i--) {
//元素中的值为几,就说明有多少个相同值的元素,就输出几遍
for (int j = 0; j < buckets[i]; j++) {
System.out.println(i);
}
}
}
}
下面来看看调用的测试代码。
package BucketSort;
public class SortTest {
public static void main(String[] args) {
testBucketSort();
}
/*桶排序*/
private static void testBucketSort() {
int[] array = {5,9,1,9,5,3,7,6,1};
BucketSort bucketSort = new BucketSort(11,array);
bucketSort.sort();
bucketSort.print();
}
}
桶排序的性能及特点
桶排序实际上只需要遍历一遍所有的待排序元素,然后依次放入指定的位置。如果加上输出排序的时间,那么需要遍历所有的桶,时间复杂度就是O(n+m),其中,n为待排序的元素的个数,m为桶的个数。这是相当快速的排序算法,但是对于空间的消耗来说有点太大了。
比如我们对1、10、100、1000这四个元素排序,那么我们需要长度为1001的数组用来排序,如果是对1、1000、10000排序呢?我们发现,当元素的跨度范围越大时,空间的浪费就越大,即使只有几个元素,但是这个范围才是空间的大小。所以桶排序的空间复杂度是O(m),其中m为桶的个数,待排序元素分布越均匀,也就是说当元素能够非常均匀地填满所有的桶时,这个空间的利用率是最好的。不过这种情况并不多见,在多数情况下,数据并不会均匀地分布。
通过上面的性能分析,我们可以知道桶排序的特点,那就是速度快、简单,但是也有相应的弱点,那就是空间利用率低,如果数据跨度过大,则空间可能无法承受,或者说这些元素并不适合使用桶排序算法。