基本思想: 将数组分到有限数量的桶子里。每个桶子再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)。桶排序是鸽巢排序的一种归纳结果。当要被排序的数组内的数值是均匀分配的时候,桶排序使用线性时间(Θ(n))。但桶排序并不是 比较排序,他不受到 O(n log n) 下限的影响。
例如要对大小为[1..1000]范围内的n个整数A[1..n]排序,可以把桶设为大小为10的范围,具体而言,设集合B[1]存储[1..10]的整数,集合B[2]存储(10..20]的整数,……集合B[i]存储((i-1)*10, i*10]的整数,i = 1,2,..100。总共有100个桶。然后对A[1..n]从头到尾扫描一遍,把每个A[i]放入对应的桶B[j]中。 然后再对这100个桶中每个桶里的数字排序,这时可用冒泡,选择,乃至快排,一般来说任何排序法都可以。最后依次输出每个桶里面的数字,且每个桶中的数字从小到大输出,这样就得到所有数字排好序的一个序列了。
假设有n个数字,有m个桶,如果数字是平均分布的,则每个桶里面平均有n/m个数字。如果对每个桶中的数字采用快速排序,那么整个算法的复杂度是O(n+m*n/m*log(n/m))=O(n+nlogn-nlogm) 从上式看出,当m接近n的时候,桶排序复杂度接近O(n) ,当然,以上复杂度的计算是基于输入的n个数字是平均分布这个假设的。这个假设是很强的,实际应用中效果并没有这么好。如果所有的数字都落在同一个桶中,那就退化成一般的排序了。
演示:
Java代码:
import java.util.Arrays;
public class LinearTimeSorter {
/**
* 桶排序,它假设数据均匀地落在区间[min,max]中。不像《算法导论》那样假设数据
* 是分布在[0, 1)之间的浮点数,这里假设的是整数。
*/
public static void bucketSort(int[] a, int min, int max) {
Node[] nodes = new Node[a.length];
for (int i = 0; i < a.length; i++) {
int v = a[i];
int bucketIndex = a.length * (v - min) / (max - min + 1);
if (nodes[bucketIndex] == null) {
nodes[bucketIndex] = new Node(v);
} else {
Node prev = null;
Node node = nodes[bucketIndex];
while (node != null && v > node.value) {
prev = node;
node = node.next;
}
if (prev == null) {
nodes[bucketIndex] = new Node(v, nodes[bucketIndex]);
} else {
prev.next = new Node(v, node);
}
}
}
for (int i = 0, j = 0; i < nodes.length; i++) {
Node node = nodes[i];
while (node != null) {
a[j++] = node.value;
node = node.next;
}
}
}
private static class Node {
private int value;
private Node next;
Node(int value) { this.value = value; }
Node(int value, Node next) { this.value = value; this.next = next; }
}
public static void main(String[] args) {
int[] a = new int[] { 7, 5, 13, 12, 3, 8, 0, 3, 15, 4};
bucketSort(a, 0, 15);
System.out.println(Arrays.toString(a));
}
}