内容介绍
桶排序简介
前面学过计数排序,计数排序是一个非基于比较的排序算法,它的优点在于在对必定范围内的整数排序时,它的复杂度为Ο(n+k)。java
所谓桶就是存放多个数据的容器,桶排序也是非基于比较的排序算法,将待排序数据按照必定的规则存放到对应的桶中,再进行排序与合并。桶排序能够对必定范围内的数据包括小数进行排序。桶排序能够突破基于比较排序算法的时间复杂度O(nlogn)。算法
桶排序的思想
将待排序数据分配到有限数量的桶里。每一个桶再单独进行子排序,最后按顺序将多个桶中的数据进行合并,排序完成。当待排序的数组内的数值是均匀分配的时候,桶排序的时间复杂度为O(n)。编程
桶排序动画演示
假设有10我的给某个商品进行评分,评分的范围是0~1之间的小数,例如{0.6, 0.85, 0.9, 0.5, 0.35, 0.2, 0.1, 0.85, 0.8, 0.5} 这10个数据。通常没有特殊要求排序算法都是升序排序,小的在前,大的在后,效果以下: 数组
桶排序分析
经过上面动画能够看出桶排序分为4个步骤:微信
- 划分合适数量的桶。
- 将全部待排序数据放入到对应的桶中。
- 使用合理的算法对每一个非空桶进行子排序。
- 按顺序将每一个桶中数据进行合并。
第一步:划分合适数量的桶动画
关于如何划分合适数量的桶,根据不一样规模和不一样范围的数据,咱们采起不一样的划分方式。目前数据都是0到1之间小数,数据比较均匀,咱们等分红4个桶,每一个桶的范围都是0.25,效果以下: 3d
第二步:将全部待排序数据放入到对应的桶中code
遍历待排序数据,将每一个数据放入对应范围的桶中,效果以下: blog
第三步:使用合理的算法对每一个非空桶进行子排序排序
待排序数据划分到不一样桶中后,每一个桶中的数据仍是无序的,以下图所示: 从上图能够看到,第二个桶不用排序,其余桶须要进行排序,分别对每一个桶中的数据再使用合适的排序算法,好比快速排序。排序后效果以下图:
第四步:4.按顺序将每一个桶中数据进行合并
合并前效果以下图: 如今范围小的桶在前面,范围大的桶在后面,而且每一个桶中的数据也是从小到大排序的,所以咱们只须要从左往右将每一个桶中的数据取出放到数组中便可。取出第一个桶后的数据以下:
取出第二个桶后的数据以下:
取出第三个桶后的数据以下:
取出第四个桶后的数据以下:
当全部桶中的数据都取出后排序就完成了。
桶排序代码编写
public class BucketSort { public static void main(String[] args) { double[] arr = {0.6, 0.85, 0.9, 0.5, 0.35, 0.2, 0.1, 0.85, 0.8, 0.5}; bucketSort(arr); System.out.println("排序后: " + Arrays.toString(arr)); } public static void bucketSort(double[] arr) { // 获取最大值和最小值 double max = arr[0]; double min = arr[0]; for (int i = 1; i < arr.length; i++) { double num = arr[i]; if (num > max) max = num; else if (num < min) min = num; } // 桶的数量 int bucketNumber = 4; // 每一个桶的范围 double range = (max - min) / bucketNumber; // 1.初始化全部桶,每一个桶都是LinkedList,方便增长数据 LinkedList<Double>[] buckets = new LinkedList[bucketNumber]; for (int i = 0; i < buckets.length; i++) { buckets[i] = new LinkedList<>(); } // 2.将全部待排序数据放入到对应的桶中 for (int i = 0; i < arr.length; i++) { double num = arr[i]; int index = (int) ((num - min) / range); if (index == bucketNumber) index -= 1; // 若是这个数字正好是最大值,计算出索引就是number,会数组越界,放到最后一个桶中 buckets[index].add(num); } // 3.使用合理的算法对每一个非空桶进行子排序 for (int i = 0; i < buckets.length; i++) { // 对每一个桶中的数据进行排序 Collections.sort(buckets[i]); } // 4.按顺序将每一个桶中数据进行合并 int index = 0; for (int i = 0; i < buckets.length; i++) { LinkedList<Double> bucket = buckets[i]; for (int j = 0; j < bucket.size(); j++) { arr[index] = bucket.get(j); index++; } } } }
桶排序的局限性
若是数据通过桶的划分以后,有些桶里的数据很是多,有些很是少,很不平均,那桶内数据排序的时间复杂度就不是常量级了。在极端状况下,若是数据都被划分到一个桶里,桶排序就退化为O(nlogn)的排序算法,以下图所示: 桶排序对待排序数据的要求是很是苛刻的,适用场景是在数据分布相对比较均匀或者数据跨度范围并非很大时。若是数据跨度很是大,空间消耗就会很大。因此桶排序不多使用。
桶排序比较适合用在外部排序中
所谓的外部排序就是数据存储在外部磁盘中、数据量比较大、内存有限,没法将数据所有加载到内存中。好比说咱们有50GB的订单数据,咱们但愿按订单金额进行排序,可是咱们的内存有限只有几GB,没办法一次性把50GB的数据都加载到内存中。假设订单金额最小是0.1元,最大是10万元。能够将全部订单根据金额划分到100个桶里,第一个桶存储金额在0.1元到100元以内的订单,第二桶存储金额在101元到200元以内的订单,依次类推。每个桶对应一个文件。订单金额在0.1元到10万元之间并不必定是均匀分布的,因此50GB订单数据是没法均匀地被划分到100个文件中的。有可能某个金额区间的数据特别多,划分以后对应的文件很大,无法一次性读入内存,针对某些划分以后仍是比较大的文件,咱们能够继续划分,好比订单金额在0.1元到100元之间的比较多,咱们就将这个区间继续划分为10个小区间,0.1元到10元,11元到20元,21元到30元依次类推,而后将每一个桶对应的数据读取到内存中使用快速排序进行排序,结果保存在文件中,最后合并到已排序的文件中。
桶排序的复杂度
- 空间复杂度:数据规模为n,划分到k个桶中,总空间复杂度
O(n + k)
。 - 时间复杂度:1.获取最大值和最小值,遍历数组,操做次数为n。2.初始化全部桶,操做次数为k。3.将全部待排序数据放入到对应的桶中操做次数为n。4.每一个非空桶进行子排序使用时间复杂度为O(nlogn)的快速排序总操做次数为(k/n)*log(k/n)*k。5.按顺序将每一个桶中数据进行合并操做次数n。因此总的时间复杂度为:3n+k+(k/n)*log(k/n)*k,所以时间复杂度为:
O(n+k+logn-logk)
。 - 稳定性:稳定
总结
桶排序也是非基于比较的排序算法,将待排序数据按照必定的规则存放到对应的桶中,再进行排序与合并。桶排序能够对必定范围内的数据包括小数进行排序。桶排序能够突破基于比较排序算法的时间复杂度O(nlogn)。
桶排序的思想:将待排序数据分配到有限数量的桶里。每一个桶再单独进行子排序,最后按顺序将多个桶中的数据进行合并,排序完成。当要被排序的数组内的数值是均匀分配的时候,桶排序的时间复杂度为O(n)。
桶排序对待排序数据的要求是很是苛刻的,适用场景是在数据分布相对比较均匀或者数据跨度范围并非很大时。若是数据跨度很是大,空间消耗就会很大,若是数据都被划分到一个桶里,桶排序就退化为O(nlogn)的排序算法,因此桶排序不多使用。
桶排序比较适合用在外部排序中。
原创文章和动画制做真心不易,您的点赞就是最大的支持! 想了解更多文章请关注微信公众号:表哥动画学编程