對於經典算法,你是否也遇到這樣的情形:學時以爲很清楚,可過陣子就忘了?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),能夠簡單理解:桶的範圍大小是人爲指定的,它不隨數據規模變化,若是數據相對均勻分佈,那麼桶的個數就是核心影響因子了。
桶排序,要作到能分分鐘手寫出來,是須要掌握其排序原理的。核心是如何劃分區間和元素歸類,一旦理解就容易寫出來,不須要死記硬背的。
但願有所幫助,本文完。
本系列已經發表文章: