【数据结构与算法】2 - 11 桶排序与计数排序
§2-11 桶排序与计数排序
2-11.1 桶排序(Bucket sort)
排序思想:将待排序的序列中的关键字分散到各个桶中,先保证后一个桶的所有元素均比前一个桶的所有元素大,即 “桶间有序”,然后再使用其他排序算法对桶内元素排序,最终使得整个序列有序。
该算法的关键在于桶的划分以及关键字到桶的映射。
算法实现:使用 ArrayList
//排序入口
public static void bucketSort(int[] arr) {
//分块
//先找到最大、最小值
int maxValue = maxValue(arr);
int minValue = minValue(arr);
//确定分块区间和个数
int bucketCount = (int) Math.sqrt((maxValue - minValue)) + 1;
ArrayList[] buckets = new ArrayList[bucketCount];
//防止元素为空
for (int i = 0; i < buckets.length; i++) {
buckets[i] = new ArrayList<>();
}
//遍历数组,将元素放进分块内
for (int i = 0; i < arr.length; i++) {
int index = getBucketIndex(arr[i],bucketCount,maxValue,minValue);
buckets[index].add(arr[i]);
}
//对分块内排序:使用Collections.sort()升序排序
for (int i = 0; i < buckets.length; i++) {
Collections.sort(buckets[i]);
}
//将结果复制到原数组
int pos = 0; //源数组索引
for (int i = 0; i < buckets.length; i++) {
for (int j = 0; j < buckets[i].size(); j++) {
arr[pos++] = (Integer) buckets[i].get(j);
}
}
}
//分块索引映射
private static int getBucketIndex(int value, int bucketCount, int max, int min) {
//数组区间长度
int range = max - min;
//分块区间长度(可能不足)
int length = range / bucketCount + 1;
//分块索引
int index = -1;
//先直接排除位于最值的情况
if (value == max) {
index = bucketCount - 1;
} else {
//计算索引
for (int i = 0; i < bucketCount; i++) {
//分块区间边界
int start = min + i * length;
int end = Math.min(start + length, max);
//桶范围区间,左闭右开
if (start <= value && value < end) {
index = i;
break;
}
}
}
return index;
}
//寻找数组最小值
private static int minValue(int[] arr) {
int minIndex = 0;
for (int i = 1; i < arr.length; i++) {
if (arr[minIndex] > arr[i])
minIndex = i;
}
return arr[minIndex];
}
//寻找数组最大值
private static int maxValue(int[] arr) {
int maxIndex = 0;
for (int i = 1; i < arr.length; i++) {
if (arr[maxIndex] < arr[i]) {
maxIndex = i;
}
}
return arr[maxIndex];
}
2-11.2 计数排序(Counting sort)
排序思想:计数排序适用于数据量不大、数据取值范围不大的情况。首先遍历数组,记录数组中不同元素的出现次数。然后根据期望顺序,根据计数器中的数据完成对原数组的排序。
以一个长度为 20,内含 0 ~ 10 的随机数数组为例,其内容一定为 0 ~ 10 的整数,因此计数器数组长度为 11,分别记录 0 到 10 元素的出现次数。然后按计数器索引顺序遍历计数器,完成对原数组的排序。
算法实现:以上述情景为例
public static void countSort(int[] arr) {
//计数器
int[] counter = new int[11];
//遍历并计数
for (int i = 0; i < arr.length; i++) {
counter[arr[i]]++;
}
System.out.println("计数器:" + Arrays.toString(counter));
//根据计数器排序数组
int index = 0; //原数组索引
for (int i = 0; i < counter.length;) {
//若当前计数器不为零,迭代计数器中计数
if (counter[i] != 0) {
arr[index++] = i;
counter[i]--;
} else {
//直至计数器清零或当前无该元素
i++;
}
}
}
注意:
- 该算法适用于一定范围内的整数排序,且取值范围不大的情况下;
- 其性能在某些情况下会快于 \(O(n \log n)\) 的排序,例如快速排序、归并排序。
浙公网安备 33010602011771号