---
# System prepended metadata

title: 基數排序(Radix Sort)

---

# 基數排序(Radix Sort)

* 基數排序(Radix Sort)屬於分配式排序(Distribution Sort)，又稱 **桶排序(Bucket Sort)** 或Bin Sort，是1887年 *赫爾曼·何樂禮* 發明的。
* 實現方式是：將整數按位數切割成不同的數字，然後按每個位數分別比較。
* 基數排序法是屬於**穩定性**的排序。
* 基數排序是對傳統桶排序的擴展，速度很快。
* 基數排序是經典的空間換時間的方式，佔用記憶體很大，當對海量數據排序時容易造成**OutOfMemoryError**。
* 有負數的陣列，我們**不用**基數排序法來進行排序，若要支持負數，可以參考：https://code.i-harness.com/zh-CN/q/e98fa9 。

**何謂穩定性排序？**

當有一陣列值為 `[1, 3, 7, 1, 3]`
排序後結果為 `[1, 1, 3, 3, 7]`
排序後第一個1 就是原陣列`arr[0]` 的 1，第二個 1 是原陣列`arr[1]` 的 1 ，也就是說，即使是相同值的元素排序前後次序依然不變，這就是穩定性排序。

## 基本思想

1. 將所有待比較數值統一為同樣的位數長度，位數較短的數前面補零。
1. 然後，從最低位開始，依次進行一次排序。
1. 這樣從最低位排序一直到最高位排序完成以後，數列就變成一個有序序列。
2. 要做多少輪排序取決於所有元素中最高位數元素的位數。

## 舉例說明

設原始陣列為 `{53,  3,  542,  748,  14,  214}`，請使用基數排序為由小到大。

**第一輪：**
1. 將每個元素的個位數取出，依個位數放到對應的桶(一個一維陣列)。
2. 按照這個桶的順序(一維陣列的索引依次取出數據，放入原來陣列)。

![](https://i.imgur.com/WfB1wma.png)

第一輪後，排序結果為 `{542, 53, 3, 14, 214, 748}`

**第二輪：**
1. 將每個元素的十位數取出，依十位數放到對應的桶，沒有十位數就補零。
2. 按照這個桶的順序(一維陣列的索引依次取出數據，放入原來陣列)。

![](https://i.imgur.com/nYRjqI0.png)

第二輪後，排序結果為 `{3, 14, 214, 542, 748, 53}`

**第三輪：**
1. 將每個元素的百位數取出，依百位數放到對應的桶，沒有百位數就補零。
2. 按照這個桶的順序(一維陣列的索引依次取出數據，放入原來陣列)。

![](https://i.imgur.com/dbx8Ak2.png)

第三輪後，排序結果為 `{3, 14, 53, 214, 542, 748}`

此時，已經由小到大排序完畢。

## 程式碼實現

```java=
package Sort;

import java.util.Arrays;

public class RadixSort {
    public static void main(String[] args) {
        int arr[] = {53,  3,  542,  748,  14,  214};
        radixSort(arr);
    }

    public static void radixSort(int[] arr) {
        // 得到陣列中最大的數的位數
        int max = arr[0];
        for(int i = 1; i < arr.length; i++) {
            if(arr[i] > max) {
                max = arr[i];
            }
        }
        
        int maxLength = (max + "").length(); 

        // 定義一個二維陣列,表示10個桶,每個桶就是一個一維陣列
        // 1. 二維陣列包含10個一維陣列
        // 2. 為了防止在放入數的時候，數據溢出，則每個一維陣列大小定為arr.length => 空間換時間
        int[][] bucket = new int[10][arr.length];

        // 定義為一個一維陣列,紀錄各個桶每次放入的數據個數
        int[] bucketElementCounts = new int[10];

        // 針對每個元素的各位數進行排序處理
        for(int i = 0 , n = 1; i < maxLength; i++, n *= 10) {
            for(int j = 0; j < arr.length; j++) {
                // 取出每個元素的的對應位數的值
                int digitOfElement = arr[j] / n % 10;
                // 放入到對應的桶中
                bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
                bucketElementCounts[digitOfElement]++ ;
            }
    
            // 遍歷每個桶, 並將桶中數據放入到原陣列
            int index = 0;
            for(int k = 0; k < bucketElementCounts.length; k++) {
                // 如果桶中有數據才放入到原陣列
                if(bucketElementCounts[k] != 0) {
                    // 遍歷該桶 放入
                    for(int l = 0; l < bucketElementCounts[k]; l++) {
                        arr[index++] = bucket[k][l];
                    }
                }
                // 每輪處理後要將 bucketElementCounts 清零
                bucketElementCounts[k] = 0;
            }
            System.out.println("第 "+(i+1)+" 輪排序後為："+Arrays.toString(arr));
        }
    }
}
```

output
```java=
第 1 輪排序後為：[542, 53, 3, 14, 214, 748]
第 2 輪排序後為：[3, 14, 214, 542, 748, 53]
第 3 輪排序後為：[3, 14, 53, 214, 542, 748]
```

## 速度測試

```java=
public static void main(String[] args) {
        // int arr[] = {53,  3,  542,  748,  14,  214};
        int arr[] = new int[80000];
        for(int i = 0; i < 80000; i++) {
            arr[i] = (int)(Math.random() * 80000);
        }
        Instant startTime = Instant.now();
        radixSort(arr);
        Instant endTime = Instant.now();
        System.out.println("共耗時：" + Duration.between(startTime, endTime).toMillis() + " 毫秒");
}
```

output
```java=
共耗時：15 毫秒
共耗時：15 毫秒
共耗時：15 毫秒
共耗時：15 毫秒
```

測了四次都是很穩定的 15ms 就能排序八萬筆數據。