一天一演算法 - 基數排序
介紹
- 比較型排序和非比較型排序
首先,比較型排序和非比較型排序有何不同呢?很簡單,如果在排序過程中,需要比較陣列中的元素大小,然後將元素放置在最終位置,這稱之為比較型排序。舉個簡單的例子來說,對於選擇排序,排序的思想就是從第一個位置開始,找到陣列中最小的元素放進去,那麼怎麼找到最小的元素呢?這個時候就需要設定一個 min
變數,在陣列中依次比較,不斷地替換 min
(如果元素的值小於 min
) 直到陣列最後的一個元素。
一個比較型排序的比較次數直接影響演算法的效能,比如選擇排序的比較次數就與 (N^2) 成正比。
非比較型排序不需要經過比較就可以將一個陣列變成有序的,從理論上來說,它應該快於任何比較型演算法,可事實上卻不是這樣!但是值得注意的是,比較型排序演算法的時間複雜度是不可能會突破 O(NlogN)
的,但是非比較型演算法比如基數排序在某些情況下卻可以突破下界,達到 O(N)
。
- 穩定排序和不穩定排序
穩定與不穩定該怎麼鑑定呢?陣列中有任意兩個元素 a[i]
和 a[j]
, 而且 a[i]
和 a[j]
滿足條件 a[i] == a[j] && i > j
,如果在排序後,i > j
仍然成立,那麼這種排序就是穩定排序,否則是不穩定排序。
基數排序是一種穩定排序演算法。
演算法思想
將所有待比較的數值統一為相同位數,長度不夠的在高位補0,然後從低位開始依次進行排序,這樣等排到最高位的時候,整個陣列自然就是有序的了。
前面我們說了,基數排序是一種非比較型排序,那麼我們依賴怎樣的手段對資料進行排序呢?
不論個位,十位還是百位,其數值都在 [0, 9] 這個區間內,所以我們首先宣告一個陣列 backet[10]
,然後再將 bucket[0] ... bucket[9]
宣告為陣列,這樣的話,我們就可以對個位進行排序,因為個位為 0 的都被放在 bucket[0]
這個陣列中,個位為 1 的都被放在 bucket[1]
這個陣列裡...
當進行完個位的排序時,按照順序將 bucket[]
元素重新推入原陣列中,然後將 bucket
陣列清空,以便進行十位的排序。
如果看了以上文字你還是渾渾噩噩的,那麼就來舉個簡單的例子,比如我們對以下陣列進行排序。
[1, 21, 89, 110, 56]
首先,我們宣告一個陣列。
var bucket = [];
for(var i = 0; i < 10; i++) {
bucket[i] = [];
}
[1, 21, 89, 110, 56]
的個位分別是 [1, 1, 9, 0, 6]
, 很顯然,對個位排序後,bucket[0] = [110], bucket[1] = [1, 21], bucket[6] = [56], bucket[9] = [89]
, 將這些數依次推入原陣列,這個時候陣列為 [110, 1, 21, 56, 89]
[110, 1, 21, 56, 89]
的十位分別為 [1, 0, 2, 5, 8]
,對十位進行排序後,bucket[0] = [1], bucket[1] = [110], bucket[2] = [21], bucket[5] = [56], bucket[8] = [89]
, 將這些數依次推入原陣列,這個時候陣列為 [1, 110, 21, 56, 89]
。
[1, 110, 21, 56, 89]
的百位分別是 [0, 1, 0, 0, 0]
,對百位進行排序後,bucket[0] = [1, 21, 56, 89], bucket[1] = [110]
,將這些數依次推入原陣列,這個時候陣列為 [1,21,56,89,110]
。
可見,這個時候陣列就變得有序了,而且和比較型排序不同的是,我們並沒有與陣列中的其它元素進行比較,只是根據自己各個位數的值讓其放進不同的陣列中。
實現 (JavaScript)
/*
* @Author: hwaphon
* @Date: 2017-03-10 13:07:02
* @Last Modified by: hwaphon
* @Last Modified time: 2017-03-10 13:34:52
*/
function radixSort(array) {
if (array.length <= 1) {
return array;
}
var length = array.length,
i,
j,
str,
k,
t,
loop,
bucket = [],
max = array[0];
for (i = 1; i < length; i++) {
if (array[i] > max) {
max = array[i];
}
}
loop = (max + '').length;
for (i = 0; i < 10; i++) {
bucket[i] = [];
}
for (i = 0; i < loop; i++) {
for (j = 0; j < length; j++) {
str = array[j] + '';
if (str.length - 1 >= i) {
k = parseInt(str[str.length - i - 1]);
bucket[k].push(array[j]);
} else {
bucket[0].push(array[j]);
}
}
array.splice(0, length);
for (j = 0; j < 10; j++) {
t = bucket[j].length;
for (k = 0; k < t; k++) {
array.push(bucket[j][k]);
}
bucket[j] = [];
}
}
return array;
}
總結
剛才我們介紹的基數排序是從低位開始進行排序的,這種排序稱之為最低位優先排序(LSD), 還有一種最高位優先排序(MSD)。LSD的基數排序適用於位數小的數列,如果位數多的話,使用MSD的效率會比較好。
我在我電腦上對比了基數排序與快速排序的效率,無論我選擇怎樣的資料,總是快速排序更快一點,如果看到我部落格的人知道原因或者知道基數排序適用情況請告與我知曉,感激不盡。
還有一點,因為快速排序是原地排序,而基數排序還需要額外的記憶體消耗,所以在選擇排序演算法時一定要考慮這一點!
擴充閱讀:
相關文章
- 排序演算法__基數排序排序演算法
- 【演算法】基數排序演算法排序
- 每天一道演算法題:求兩個排序陣列的中位數演算法排序陣列
- 看動畫學演算法之:排序-基數排序動畫演算法排序
- C#基數排序演算法C#排序演算法
- 資料結構與演算法——排序演算法-基數排序資料結構演算法排序
- 【資料結構與演算法】非比較排序(計數排序、桶排序、基數排序)資料結構演算法排序
- 計數排序、桶排序和基數排序排序
- 八大排序演算法—16張圖搞懂基數排序排序演算法
- 經典十大排序演算法(含升序降序,基數排序含負數排序)排序演算法
- 每天一道演算法題:顛倒整數演算法
- 排序演算法__計數排序排序演算法
- 基數排序排序
- 每天一道演算法題--排序之桶排序實現求排序後相鄰最大差值問題演算法排序
- 計數排序vs基數排序vs桶排序排序
- 桶排序和基數排序排序
- 演算法基礎:排序演算法:選擇排序演算法排序
- java 基數排序Java排序
- 【資料結構與演算法】內部排序之五:計數排序、基數排序和桶排序(含完整原始碼)資料結構演算法排序原始碼
- 歸併排序和基數排序排序
- 每天一道演算法題系列十三之羅馬數字轉整數演算法
- 經典演算法之基數排序兩種實現演算法排序
- 用 PHP 在 力扣 上演算法 [兩數之和]{一天一更}PHP力扣演算法
- 用 PHP 在 力扣 刷演算法 [兩數相加]{一天一更}PHP力扣演算法
- 排序演算法-N個正整數排序排序演算法
- 撲克牌排序:基於基數排序的方法排序
- 【筆記】基數排序筆記排序
- 基數排序 LSD py排序
- 排序(2)--選擇排序,歸併排序和基數排序排序
- 基於桶的排序之基數排序以及排序方法總結排序
- 排序演算法(一)排序演算法
- JavaScript計數排序演算法JavaScript排序演算法
- 基礎演算法之排序演算法演算法排序
- 夯實基礎:排序演算法之堆排序排序演算法
- 基於桶的排序之計數排序排序
- Python八大演算法的實現,插入排序、希爾排序、氣泡排序、快速排序、直接選擇排序、堆排序、歸併排序、基數排序。Python演算法排序
- 【Java資料結構與演算法】第八章 快速排序、歸併排序和基數排序Java資料結構演算法排序
- 【演算法-排序之一】氣泡排序演算法排序