手写算法并记住它:桶排序

对于经典算法,你是否也遇到这样的情形:学时以为很清楚,可过阵子就忘了?javascript

本系列文章就尝试解决这个问题。java

研读那些排序算法,细品它们的名字,其实都很贴切。算法

好比桶排序,一提起“桶”,我就想到了垃圾分类。数组

桶排序就是先分类,即把数据放进相应的桶里,而后对每一个桶进行局部排序,最后再把桶合并一下就好了。分布式

上图演示了该算法的整体流程。分为三步,分类,排序和合并。post

该算法的核心是如何分类,也就是,如何划分桶并把元素放进相应的桶里。优化

它的分类是按区间分类。图中元素最小值是1,最大值是9,所以全部元素位于属于区间【1,9】。假设桶(区间)的范围大小是3,那么就有3个桶,具体是:【1,3】、【4,6】、【7,9】。ui

用代码体现一下:spa

let array = [3, 8, 6, 1, 5, 7, 9, 2, 4]
let min = Math.min(...array)
let max = Math.max(...array)
let size = 3
let count = Math.floor((max - min) / size) + 1
let buckets = []
for (let i = 0; i < count; i++) {
  buckets.push([])
}
console.log(buckets) // [ [], [], [] ]
复制代码

整体来讲代码比较简单,须要注意的是count的计算。3d

桶有了,接下来就是把每一个元素归类。

for (let v of array) {
  let num = Math.floor((v - min) / size)
  buckets[num].push(v)
}
console.log(buckets) // [ [ 3, 1, 2 ], [ 6, 5, 4 ], [ 8, 7, 9 ] ]
复制代码

其中,num表示桶的序号,与count同样,也是经过偏移量来计算的。由于下标是从0开始的,所以这里没有再加1。

关键问题解决了,排序和合并就相对简单了。

排序用其余任一排序算法均可以,好比数据量不太大的时候,插入排序就比较适合。

由于区间原本就是有序的,合并时只需直接链接这些数组便可。

let result = []
for (bucket of buckets) {
  result.push(...insertionSort(bucket)) 
}
console.log(result) // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
复制代码

至此,桶排序原理和实现已经说完了。查看完整代码:codepen

其实还有优化的空间,好比min和max能够经过一次循环肯定出来。另一点是,咱们知道插入排序的思想是把待排序元素插入到已排序序列里,所以能够在第一步分类时就直接插入就行。

这里总结一下,桶排序是分布式排序,适合处理大批量数据。须要额外空间,是外部排序。桶排序是否稳定,取决于第二步排序算法的选择。时间复杂度是线性级O(n),能够简单理解:桶的范围大小是人为指定的,它不随数据规模变化,若是数据相对均匀分布,那么桶的个数就是核心影响因子了。

桶排序,要作到能分分钟手写出来,是须要掌握其排序原理的。核心是如何划分区间和元素归类,一旦理解就容易写出来,不须要死记硬背的。

但愿有所帮助,本文完。



本系列已经发表文章:

相关文章
相关标签/搜索