Java经典算法——桶排序

1、什么是桶排序

桶排序是一种稳定的排序算法。它的工作原理是将序列中的元素分布到一定数量的桶内,然后分别对每个桶内的元素进行排序,最后再将各个桶内的有序子序列放回原始序列中。对于单个桶内的元素排序,我们可以使用别的排序算法,也可以递归使用桶排序。一般来说,对于单个桶内的元素,使用插入排序算法对它们进行排序。

2、问题

假设目前有包含 100,200,300,400,500,600 共 6 个数字的一个随机顺序的集合。我们需要对这个集合进行桶排序。
在这里插入图片描述

3、分析桶排序

桶排序需要思考两个问题,有多少个桶,每个桶多大也就是每个桶放多少。

首先我们设置桶的大小为 2 。

然后一共有6个数字 除以每个桶的 大小 就是需要多少个桶,在这里我们需要3个桶。

现在有桶了,我们需要一个方法找到元素对应的桶。我们使用一个计算公式来计算所属哪个桶:f(x) = (int)((x - min) / (max - min + 1.0) * 桶个数)。

(x - min) / (max - min + 1.0) 计算出的结果 是一个大于等于0,最大趋近于1且不等于1的值,然后乘以桶的个数进行强转整数类型,就可以计算出0-9之间的一个数字,就代表具体的桶编号。之所以用这个公式,是因为这个公式会随着 x 的变大而变大,可以保证桶内数据是在某个范围内,且后边的桶比前边的桶内存放的数据要大。

每个桶都存放完成后,遍历10个桶,每个桶都进行插入排序。排序完一个桶后就将这个桶中的数据放回原先的集合中。

4、步骤
  1. 设置几个数组作为空桶。
  2. 从左到右遍历待排序序列,把每个元素都放到对应的桶中
  3. 对每个不是空的桶进行排序
  4. 依次取出所有桶中的元素放回原序列
5、根据《算法》中描述每个桶的大小在 5~10 之间性能最优。

时间空间复杂度分析

空间复杂度
因为排序过程中用到了一个辅助桶来存储元素,所以空间复杂度是O(n)

时间复杂度
有 n 个待排序元素,均匀地将这些元素划分到 bucketCount 个桶内,每个桶里就有 k=n/bucketCount 个元素。每个桶内部使用快速排序,时间复杂度为 O(k logk)。m 个桶排序的时间复杂度就是 O(bucketCount k logk),因为 k=n/bucketCount,所以整个桶排序的时间复杂度就是 O(nlog(n/bucketCount))。当桶的个数 bucketCount 接近数据个数 n 时,log(n/bucketCount) 就是一个非常小的常量,这个时候桶排序的时间复杂度接近 O(n)。所以时间复杂度为O(n)

桶排序的适应场景

桶排序对数据要求较高,适用于数据是均匀分布的情况,这样可以让分布到各个桶内的元素数量相当。而不是被集中分配到其中一个桶或几个桶。

比较适合用在外部排序中。

因为数据量比较大,数据存储在外部磁盘中,无法一次性全部放入内存进行排序,一部分一部分的读入内存再写入磁盘,这种方式称为外部排序。

代码实现

Java实现代码

public class BucketSort {
    private void insertionSort(List<Integer> arr) {
      if (arr == null || arr.size() == 0) return;
      for (int i = 1; i < arr.size(); ++i) {
        int cur = arr.get(i);
        int j = i - 1;
        while (j >= 0 && arr.get(j) > cur) {
          arr.set(j+1, arr.get(j));
          --j;
        }
        arr.set(j+1, cur);
      }
    }
    private int bucketSize;
    public BucketSort(int bucketSize) {
      this.bucketSize = bucketSize;
    }
    public void sort(int[] arr) {
      if (arr == null || arr.length == 0) return;
      int max = arr[0], min = arr[0];
      for (int num: arr) {
        if (num > max) max = num;
        if (num < min) min = num;
      }
      int bucketCount = arr.length / bucketSize;
      List<List<Integer>> buckets = new ArrayList<>(bucketCount);
      for (int i = 0; i < bucketCount; ++i)
        buckets.add(new ArrayList<>());
      for (int num: arr) {
        int idx = (int)((num - min) / (max - min + 1.0) * bucketCount);
        buckets.get(idx).add(num);
      }
      int idx = 0;
      for (List<Integer> bucket: buckets) {
        insertionSort(bucket);
        for (int num: bucket)
          arr[idx++] = num;
      }
    }
 }

JavaScript实现代码

def Bucket_Sort(array, bucketsize):
    minValue = min(array)
    maxValue = max(array)
    res = []
    bucketcount = (maxValue - minValue + 1) // bucketsize
    bucket_lists = [[] for i in range(bucketcount)]
    
    for i in array:
        bucket_index = (i - minValue) // bucketsize
        bucket_lists[bucket_index].append(i)
    # 桶内排序
    for j in bucket_lists:
        Quick_Sort_2(j, 0, len(j)-1)    

    for j in bucket_lists:
        if len(j) != 0:
            res.extend(j)
    return res

进阶之路

查找和排序算法是算法的入门知识,其经典思想可以用于很多算法当中。因为其实现代码较短,应用较常见。所以在面试中经常会问到排序算法及其相关的问题。但万变不离其宗,只要熟悉了思想,灵活运用也不是难事。

一般在面试中最常考的是快速排序和归并排序,并且经常有面试官要求现场写出这两种排序的代码。对这两种排序的代码一定要信手拈来才行。还有插入排序、冒泡排序、堆排序、基数排序、桶排序等。

面试官对于这些排序可能会要求比较各自的优劣、各种算法的思想及其使用场景。还有要会分析算法的时间和空间复杂度。通常查找和排序算法的考察是面试的开始,如果这些问题回答不好,估计面试官都没有继续面试下去的兴趣了。

所以想开个好头就要把常见的排序算法思想及其特点要熟练掌握,有必要时要熟练写出代码。

在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值