【算法】——基數排序,一種空間換時間的穩定排序算法

基數排序是一種非比較型整數排序算法,其原理是將整數按位數切割成不同的數字,然後按每個位數分別比較

基數排序是一種穩定排序算法,在某些時候,基數排序的效率高於其它的穩定排序

基數排序的方式可以採用最低位優先LSD(Least sgnificant digital)法或最高位優先MSD(Most sgnificant digital)法,LSD就是從個位先進行排序,然後十位、百位......MSD則相反

 

舉例分析

有一組無序序列,我們將其用基數排序算法進行排序(LSD)

第一次排序

  • 我們按個位上的數字來進行劃分
  • 個位上的數字爲0,就放入第0個桶中,個位上的數字爲1,就放入第一個桶中......

 

  • 按個位劃分之後,我們再將這些數據依次取出來(取出之後已經是個位上的數字有序)

 

 

第二次排序

  • 然後再將以上數據,按十位上的數字大小進行劃分

  •  按十位劃分之後,我們再將這些數據依次取出來(取出之後已經是個位上的數字和十位上的數字都有序)

 

 

第三次排序

  •  然後再將以上數據,按百位上的數字大小進行劃分

 

  •  按百位劃分之後,我們再將這些數據依次取出來(取出之後已經是個位上的數字、十位上的數字、百位上的數字都有序)

  • 排序結束,此時已經是一個有序序列

在以上排序中我們可以看到,共進行了三次排序,爲什麼?

因爲整個數組中的元素,最大元素就是999,他是3位的,所以需要進行3次排序

假如最大元素時1314,是4位的,那麼我們就需要進行4次排序

 

總結一下

  • 先找到數組中最大元素的位數,判斷要進行幾次排序
  • 然後先排個位,再排十位......
  • 其中每趟排序中,先依次劃分元素,然後再依次取出元素

注意:劃分元素和取出元素時,一定是按順序進行的,纔可保證排序成功(因爲寫代碼的時候,肯定是按順序遍歷的,所以無需特殊操作,只需正常寫即可)

 

Java代碼 

package algorithm;

import java.util.Arrays;

public class Sort {

    // 基數排序
    private static int[] radixSort(int array[]) {
        // 10個桶,每個桶最多放全部
        int[][] temp = new int[10][array.length];
        // 存放每個桶裏面放了幾個元素
        int[] tempIndex  = new int[10];

        //  獲得數組中最大的數字
        int max = getMax(array);
        // 獲得最大數字的位數,
        int maxLength = (max+"").length(); // 轉換成字符串,再獲取長度即可

        // 共比較最大數字位數次
        for (int i = 0, n = 1; i < maxLength ; i++, n *= 10) {
            // 一次排序,將對應元素放入對應數組中
           for (int j = 0 ; j < array.length ; j++) {
               // 取個位:(520 / 1) % 10 = 0
               // 取十位:(520 / 10) % 10 = 2
               // 取百位:(520 / 100) % 10 = 5
               // 所以第一次比較個位 n = 1,第二次比較十位 n = 10...
               int remainder = (array[j] / n) % 10;
               // 把當前元素放入指定數組中的指定位置
               // 取出來的餘數是幾,就放入第幾個桶中(數組的第幾行)
               // 然後放入那個桶中的 tempIndex[remainder] 位置
               temp[remainder][tempIndex[remainder]] = array[j];
               tempIndex[remainder]++;
           }

            // 用於取出數據時放入原數組中的下標
            int index = 0;
            // 將元素全部依次取出
            for (int k = 0 ; k < tempIndex.length ; k++) { // k 代表第幾個桶
               if (tempIndex[k] != 0) { // 說明這個桶中放入了元素,將其取出
                   // 取出一個桶中的數據
                   for (int w = 0 ; w < tempIndex[k] ; w++) {
                       array[index] = temp[k][w]; // 替換原數組元素
                       index++;
                   }
                   tempIndex[k] = 0; // 清零
               } // 取完一個桶的元素
            } // 取完全部桶的元素
        }
        return array;
    }

    // 獲得數組中最大的數字
    private static int getMax(int array[]) {
        int max = 0;
        for (int anArray : array) {
            if (anArray > max) {
                max = anArray;
            }
        }
        return max;
    }

    public static void main(String[] args) {
        int[] array = new int[]{5, 2, 0, 13, 14, 520, 134, 1314, 9999};
        int[] result = radixSort(array);
        System.out.println(Arrays.toString(result));
    }
}

在以上代碼中,我們需要注意一下創建二維數組這個 

// 10個桶,每個桶最多放全部
int[][] temp = new int[10][array.length];

數組的大小是10 × array.length ,說明有十行, array.length 列,每行代表一個桶,而一行中有多少個列,就代表這個桶可以放多少個元素

  • 因爲我們並不知道個位爲0的有多少個元素,個位爲1的有多少個元素,十位爲9的有多少個元素......
  • 所以我們創建數組時,有多少列是不確定的
  • 而列的最大數,就是當所有元素的某位上數字都是一樣的時候,就是數組的長度
  • 所以我們這裏將其設置爲最大 array.length

如果數據少的情況下還好,但是當數據量很大的情況,爲數組指定很大的空間,而根本用不到這麼多的空間,就會有很多的空間被浪費掉了

所以我們可以先爲數組指定一些空間,如果空間不夠的話,再進行數組擴容,這樣就有效避免資源浪費

或者直接存入一維數組中,比如在遍歷個位的時候,直接將元素放入新的一維數組的指定位置中,而新的數組大小我們可以直接指定爲原數組的大小

這裏爲了看起來更直觀,所以採用二維數組模擬放置的桶(不是桶排序)

 

基數排序是一種用空間換時間的排序算法

時間複雜度

最好情況:O(k * N),k 爲數組中最大數的位數

平均情況:O(k * N)

最壞情況:O(k * N)

雖然同爲基數排序,但不同的寫法,時間複雜度也會有一些差異

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章