在這篇之前的排序法都可以用在任何可以比較的資料上,例如一個含有帳戶資料的陣列,按照每個帳戶的 ID 、更新時間、名字、帳戶餘額等等來排序。但 Radix Sort (基數排序) 較不一樣,它只可以用在數字的排序上,而且它不比大小。
以 [9, 133, 567, 44, 21, 45, 11, 561]
來說:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
21 | 133 | 44 | 45 | 567 | 9 | ||||
11 | |||||||||
561 |
[21, 11, 561, 133, 44, 45, 567, 9]
如果數字只有個位數的話,相當於它的其他位數都是零。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
9 | 11 | 21 | 133 | 44 | 561 | ||||
45 | 567 |
[9, 11, 21, 133, 44, 45, 561, 567]
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
9 | 133 | 561 | |||||||
11 | 567 | ||||||||
21 | |||||||||
44 | |||||||||
45 |
[9, 11, 21, 44, 45, 133, 561, 567]
給一個數字,回傳這個數字有幾位數,例如 digitCount(123)
回傳 3
。
function digitCount(n) {
return String(n).length;
}
或者是
function digitCount(n) {
if (num === 0) return 1;
return Math.floor(Math.log10(Math.abs(num))) + 1;
// 由於 Math.log10(0) 的情況會是 -Infinity,所以要額外做判斷。
}
給定一個只有數字的陣列,回傳其中最大數字有幾位數,例如 mostDigits([13, 56, 7899])
回傳 4
。
function mostDigits(arr) {
let max = 0;
for (let i = 0; i < arr.length; i++) {
if (String(arr[i]).length > max) {
max = String(arr[i]).length;
}
}
return max;
}
給定一個數字與位數,回傳這個數字的這個位數的數字,例如 getDigits(123, 3)
回傳 1
。
function getDigits(num, digit) {
return Math.floor(Math.abs(num) / Math.pow(10, digit - 1)) % 10;
}
接著沿用上面幾個練習來實作。
function radixSort(arr) {
const maxDigit = mostDigits(arr);
for (let k = 1; k <= maxDigit; k++) {
const digitSlots = Array(10).fill().map(() => []);
// 或是 Array.from({ length: 10 }, () => []);
for (let i = 0; i < arr2.length; i++) {
digitSlots[getDigits(arr2[i], k)].push(arr2[i]);
}
arr = digitSlots.flat();
// 或是 arr = [].concat(...digitSlots);
}
return arr2;
}
Time Complexity (Best) | Time Complexity (Average) | Time Complexity (Worst) | Space Complexity |
---|---|---|---|
O(nk) | O(nk) | O(nk) | O(n + k) |
n
= 數字總數,k
= 數字的位數
由上述實作可知數字的位數也會影響到我們第一層迴圈的執行次數。
而 k
要根據題目的情境來決定,假設說題目限定數字大範圍是 -10² ~ 10²,那麼 k
的值會是 3
可以當作是常數,相近於 O(n)。
在沒有特別用常數定義數字的大小時, k
會相近於 log(n)。解釋來自於這篇
在 k
會是常數的情境下,效能會比之前提到 O(n log n) 系列的排序都還好。
就算是特別的情境下 k ~= log(n)
也跟 O(n log n) 系列的排序幾乎持平。
空間複雜度則很簡單,除了一直儲存 n
個數字到對應的桶子之外,每個位數 (k
) 的比對都會建立桶子,所以空間複雜度是 O(n + k)。
Radix Sort 依筆者理解常用於只有整數的數字的排序,不太適用於小數點,否則還需要先找出擁有小數點最多的位數是到多少,且要從那一位開始排序。