一、算法思想
通过遍历,将待排序序列分到有限数量的桶中,然后对每个桶单独进行排序,最后将所有桶的结果输出,即为有序序列(关键问题在于,如何设置合适数量的桶)。
二、算法步骤
(1)设定合适数量的桶;
(2)遍历待排序序列,将每个元素放到对应的桶中;
(3)对每个桶(非空的桶)进行合适的排序算法;
(4)按顺序访问桶,将桶中的元素依次输出,得到有序序列(注意:对每个桶使用的排序算法要与访问桶的顺序一致,比如都为从小到大)。
三、代码实现
1、简单分桶
适合于待排序序列各元素之间的大小跨度比较小的情况:
- 桶的数目 bucketNum = max / 10 - min / 10 + 1;
- 依据(array[i] - min)/ 10的结果将各元素放入对应的桶中;
- 遍历所有桶,输出有序序列。
代码如下:
// 简单分桶
public int[] bucketSort1(int[] sourceArray) {
// 拷贝数组
int[] array = Arrays.copyOf(sourceArray, sourceArray.length);
// 找到数组中的最大、最小值,以设定合适数量的桶
int max = array[0];
int min = array[0];
for (int i = 1; i < array.length; i++) {
if (max < array[i])
max = array[i];
if (min > array[i])
min = array[i];
}
// 1、设定分桶情况
int bucketNum = max / 10 - min / 10 + 1;
List<List<Integer>> bucketList = new ArrayList<>();
// 2、创建桶
for (int i = 0; i < bucketNum; i++) {
bucketList.add(new ArrayList<Integer>());
}
// 3、将待排序数组的元素添加到对应的桶里
for (int i = 0; i < array.length; i++) {
int index = (array[i] - min) / 10;
bucketList.get(index).add(array[i]);
}
// 4、对每个桶进行插入排序,并把排好序的结果重新放回原数组
List<Integer> tempBucket = null;
int index = 0;
for (int i = 0; i < bucketNum; i++) {
tempBucket = bucketList.get(i);
int[] temp = new int[tempBucket.size()];
for(int j = 0; j < tempBucket.size(); j++){
temp[j] = tempBucket.get(j);
}
temp = insertSort(temp);// 插入排序
for(int a : temp){
array[index++] = a;
}
}
return array;
}
2、归约化分桶
适合于待排序序列各元素之间的大小跨度比较大的情况:
- 分桶情况:index = (array[i] - min) / (max - min) * array.length;
- 采用 HashMap 记录桶的情况:HashMap<Integer, ArrayList< Integer > > bucketMap = new HashMap<>()。遍历待排序序列,将所有元素进行上式处理,得到的 index 作为 key,对应的元素添加到动态数组中作为 value。
代码如下:
// 归约化分桶
public int[] bucketSort2(int[] sourceArray) {
// 拷贝数组
int[] array = Arrays.copyOf(sourceArray, sourceArray.length);
// 找到数组中的最大、最小值,以设定合适数量的桶
int max = array[0];
int min = array[0];
for (int i = 1; i < array.length; i++) {
if (max < array[i])
max = array[i];
if (min > array[i])
min = array[i];
}
// 1、设定分桶情况,将待排序数组的元素添加到对应的桶里
HashMap<Integer, ArrayList<Integer>> bucketMap = new HashMap<>();
for (int i = 0; i < array.length; i++) {
int index = (array[i] - min) / (max - min) * array.length;
// Map中不包含index,则为其创建list并添加至Map中
if(!bucketMap.containsKey(index)){
ArrayList<Integer> list = new ArrayList<>();
list.add(array[i]);
bucketMap.put(index, list);
}else{// Map中已有index,则直接将该元素添加至对应的list中
bucketMap.get(index).add(array[i]);
}
}
// 2、对每个桶进行插入排序,并把排好序的结果重新放回原数组
ArrayList<Integer> tempBucket = null;
int index = 0;
Iterator iterKey = bucketMap.keySet().iterator();
while(iterKey.hasNext()){
tempBucket = bucketMap.get(iterKey.next());
int[] temp = new int[tempBucket.size()];
for(int j = 0; j < tempBucket.size(); j++){
temp[j] = tempBucket.get(j);
}
temp = insertSort(temp);// 插入排序
for(int a : temp){
array[index++] = a;
}
}
return array;
}
附:插入排序
public int[] insertSort(int[] SourceArray){
int[] array = Arrays.copyOf(SourceArray, SourceArray.length);
// 将array[0]看做有序序列
for (int i = 1; i < array.length; i++) {
// 记录要插入的数据
int temp = array[i];
// 从已经排序的序列的最右边开始比较,找到比其小的数
int j = i;
// temp > array[j - 1]:大的往前走,降序
// temp < array[j - 1]:小的往前走,升序
while(j > 0 && temp > array[j - 1]){
array[j] = array[j - 1];
j--;
}
// 存在比其小的数,插入
if(j != i)
array[j] = temp;
}
return array;
}