算法描述

桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。为了使桶排序更加高效,我们需要做到这两点:

  1. 在额外空间充足的情况下,尽量增大桶的数量
  2. 使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中

对于桶中元素的排序,选择何种比较排序算法对于性能的影响至关重要。

示意

元素分布在桶中:
image.png
然后,元素在每个桶中排序:
image.png

算法实现

这里除了用到了桶排序之外,对桶内的元素我们使用了插入排序。
比较关键的地方是设置每个桶内容纳的元素个数,我们默认每个桶装5个元素。
知道了每个桶的容量,就可以求出在这些数值范围内有多少个桶能容纳这些元素。首先我们需要求出最大值和最小值,求出他们的差,除以每个桶的容量就可以得到一共需要多少个桶。
然后就遍历数组,求当前元素放入哪个桶中。
最后对桶中的元素进行插入排序,然后把所有桶中的元素放在一起得到最后的结果。

  1. /**
  2. * 插入排序
  3. * @param {number[]} arr 数组
  4. */
  5. const insertionSort = (arr) => {
  6. let current
  7. for (let i = 1; i < arr.length; i++) {
  8. current = arr[i]
  9. for (let j = i - 1; j >= 0; j--) {
  10. if (current < arr[j]) {
  11. arr[j + 1] = arr[j]
  12. if (j === 0) arr[j] = current
  13. } else {
  14. arr[j + 1] = current
  15. break
  16. }
  17. }
  18. }
  19. return arr
  20. }
  21. /**
  22. * 桶排序
  23. * @param {number[]} arr 数组
  24. * @param {number} bucketSize 每个桶的大小,默认为 5
  25. */
  26. const bucketSort = (arr, bucketSize = 5) => {
  27. if (!arr.length) return arr
  28. let max = Math.max.apply(null, arr)
  29. let min = Math.min.apply(null, arr)
  30. const buckets = []
  31. const bucketCount = Math.floor((max - min) / bucketSize) + 1
  32. for (let i = 0; i < bucketCount; i++) {
  33. if (!buckets[i]) buckets[i] = []
  34. buckets[Math.floor((arr[i] - min) / bucketSize)].push(arr[i])
  35. }
  36. const ret = []
  37. for (let i = 0; i < bucketCount; i++) {
  38. insertionSort(buckets[i])
  39. ret = [...ret, ...buckets[i]]
  40. }
  41. return ret
  42. }