简单桶排序
简单桶排序又称为鸽巢排序或计数排序,其基本思想如下:
假设待排序整型数组arr的元素大小范围是[0, max],
额外分配一个长度为(max+1)的整型数组bucket[max+1],数组中元素初始全部初始化为0,
那么每一个bucket数组中元素就相当于一个桶(或称为鸽巢),元素的下标相当于这个桶所要装的数值的大小,元素的值代表有多少个这个大小的值,
遍历arr,假设当前元素arr[i]的值是x,那么bucket数组中下标为x的元素加1(相当于在放大小是x的桶中放入了一个元素),如此类推,直到arr遍历完成,(此时可理解为arr已清空,但数组还在),
最后,遍历bucket数组,当遍历到元素不为0的元素时,将该元素下标从头赋值到arr中,该元素的值是几就赋值几次,直到bucket遍历完成,
此时arr数组已成为有序数组。
如下图:
Java代码实现:
1 /** 2 * 用简单桶排序算法对整型数组进行升序排序 3 * 4 * @param arr:待排序数组 5 * @param maxVal:待排序数组中元素的最大值 6 */ 7 private static void simpleBucketSort(int[] arr, int maxVal) { 8 if (arr == null || arr.length <= 1) 9 return; 10 //分配桶数组 11 int[] bucket = new int[maxVal + 1]; 12 //装桶 13 for (int i = 0; i < arr.length; i++) { 14 if (arr[i] < 0) 15 throw new RuntimeException("待排序数组中包含负数!"); 16 bucket[arr[i]]++; 17 } 18 //将桶中元素依次放回原数组 19 int index = 0; 20 for (int i = 0; i < bucket.length; i++) { 21 for (int j = 0; j < bucket[i]; j++) 22 arr[index++] = i; 23 } 24 }
特点:
1. 简单桶排序的时间复杂度是O(N),而且它完全没有比较操作,所以效率非常高。
2. 由于需要开辟新数组(桶),所以其空间复杂度非常高,其最坏空间复杂度为O(N2)。若待排序数组元素大小的范围非常大,则对空间的占用会更高。
2. 显而易见,简单桶排序的局限性非常大。首先,必须知道待排序数组元素的范围,否则无法进行。其次若待排序数组非整数或负数,同样无法操作。
真正的桶排序
基本思想:
把待排序数组划分为n个大小相同子区间(桶),每个子区间各自排序,最后合并 。
可见,简单桶排序是桶排序的一个特例,相当于子区间内只有一个数的桶排序。
但桶排序要求数据的分布必须均匀,否则可能所有数据都集中到一个子区间中,导致桶排序失去意义。
Java代码实现:
1 /** 2 * 用桶排序算法对整型数组进行升序排序 3 * 4 * @param arr:待排序数组 5 */ 6 private static void bucketSort(int[] arr) { 7 /*找出数组中的最大值和最小值*/ 8 int max = Integer.MIN_VALUE; 9 int min = Integer.MAX_VALUE; 10 for (int i = 0; i < arr.length; i++) { 11 max = Math.max(max, arr[i]); 12 min = Math.min(min, arr[i]); 13 } 14 15 /*分配桶*/ 16 int nBucket = (max - min) / arr.length + 1; //桶的个数 17 List<List<Integer>> buckets = new ArrayList<>(nBucket); //用线性表作为桶,桶内的数据结构也是线性表 18 for (int i = 0; i < nBucket; i++) 19 buckets.add(new ArrayList<>()); 20 21 /*将元素放入桶中*/ 22 for (int i = 0; i < arr.length; i++) { 23 int n = (arr[i] - min) / arr.length; //找到对应的桶号 24 buckets.get(n).add(arr[i]); //放入对应桶号的桶中 25 } 26 27 /*用其它算法对每个桶中的元素进行排序*/ 28 for (int i = 0; i < nBucket; i++) 29 Collections.sort(buckets.get(i)); //此处用API模拟使用了其它算法 30 31 /*将排序后的元素放回原数组中*/ 32 int index = 0; 33 for (List<Integer> bucket : buckets) 34 for (Integer i : bucket) 35 arr[index++] = i; 36 }
此处用线性表作为桶内的数据结构,解决了简单桶排序中,元素不能存在负数的问题。
典型应用:
1. 全国高考分数排序。高考分数为0~750,没有小数,假设全国500万考生。显然数值范围已知,数据量庞大,桶排序是一个很好的解决方法。
2. 大文件快速排序。(以后另起一篇文章探讨这个话题)