手寫算法並記住它:桶排序

對於經典算法,你是否也遇到這樣的情形:學時以爲很清楚,可過陣子就忘了?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),能夠簡單理解:桶的範圍大小是人爲指定的,它不隨數據規模變化,若是數據相對均勻分佈,那麼桶的個數就是核心影響因子了。

桶排序,要作到能分分鐘手寫出來,是須要掌握其排序原理的。核心是如何劃分區間和元素歸類,一旦理解就容易寫出來,不須要死記硬背的。

但願有所幫助,本文完。



本系列已經發表文章:

相關文章
相關標籤/搜索