桶排序是鸽巢排序的一种归纳结果。当要被排序的数组内的数值是均匀分配的时候,桶排序使用线性时间(Θ(n))。但桶排序并不是 比较排序,他不受到 O(n log n) 下限的影响。
思路:
- 将所有待比较数值统一为同样的数位长度,数位较短的数前面补零
- 依次将各个元素的最低位、次低位、…、最高位取出,分配到对应的桶中(一维数组)
- 从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。
算法实现
- 设置一个定量的数组当作空桶
- 遍历序列,依次把元素放到对应的桶中
- 对每个非空桶进行排序
- 将排序后的非空桶返回到原序列之中
public static void bucketSort(int[] arr){
// 计算最大值与最小值
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
for(int i = 0; i < arr.length; i++){
max = Math.max(max, arr[i]);
min = Math.min(min, arr[i]);
}
// 计算桶的数量
int bucketNum = (max - min) / arr.length + 1;
ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum);
for(int i = 0; i < bucketNum; i++){
bucketArr.add(new ArrayList<Integer>());
}
// 将每个元素放入桶
for(int i = 0; i < arr.length; i++){
int num = (arr[i] - min) / (arr.length);
bucketArr.get(num).add(arr[i]);
}
// 对每个桶进行排序
for(int i = 0; i < bucketArr.size(); i++){
Collections.sort(bucketArr.get(i));
}
// 将桶中的元素赋值到原序列
int index = 0;
for(int i = 0; i < bucketArr.size(); i++){
for(int j = 0; j < bucketArr.get(i).size(); j++){
arr[index++] = bucketArr.get(i).get(j);
}
}
}
public static void main(String[] args) {
int[] arr = {3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48};
System.out.println("原arr数组:" + Arrays.toString(arr));
bucketSort(arr);
System.out.println("桶排序后arr为:" + Arrays.toString(arr));
}
运行结果:
时间复杂度: O(N+c)
对于待排序序列大小为 N,共分为 M 个桶,主要步骤有:
N 次循环,将每个元素装入对应的桶中
M 次循环,对每个桶中的数据进行排序(平均每个桶有 N/M 个元素)
一般使用较为快速的排序算法,时间复杂度为 O ( N l o g N ) O(NlogN) O(NlogN),实际的桶排序过程是以链表形式插入的。
整个桶排序的时间复杂度为:
O ( N ) + O ( M ∗ ( N / M ∗ l o g ( N / M ) ) ) = O ( N ∗ ( l o g ( N / M ) + 1 ) ) O(N)+O(M*(N/Mlog(N/M)))=O(N(log(N/M)+1)) O(N)+O(M∗(N/M∗log(N/M)))=O(N∗(log(N/M)+1))
当 N = M 时,复杂度为 O ( N ) O(N) O(N)
空间复杂度: O(N+M)
桶排序中,需要创建M个桶的额外空间,以及N个元素的额外空间
稳定性:
桶排序的稳定性取决于桶内排序使用的算法