1. 程式人生 > >計數排序:時間複雜度僅為 O(n) 的排序演算法

計數排序:時間複雜度僅為 O(n) 的排序演算法

一、簡介

計算排序假設 n 個輸入元素都是 0 到 k 區間內的一個整數,其中 k 為某個整數。

基本原理:

建立一個長度為 k+1 的陣列 count[],它的 count[i] 的值對應輸入陣列中 i 出現的次數。通過遍歷一次輸入陣列並統計每個元素出現次數,最後遍歷 count[] 輸出。

二、時間複雜度

計算排序的時間複雜度為 O(n)。

計算排序不是比較排序演算法,它沒有元素之間的比較操作(判斷大於或者小於),而我們之前所瞭解到的所有的比較排序演算法的時間複雜度下界均為 Ω(n * lg n)。

三、實現

public class Main {

    public static void main(String[] args) {
        // 輸入元素均在 [0, 10) 這個區間內
        int[] arr = new int[] { 5, 4, 6, 7, 5, 1, 0, 9, 8, 1 };
        countSort(arr);
        printArray(arr);
    }

    public static void countSort(int[] arr) {
        int[] count = new int[10];
        for (int i = 0; i < arr.length; i++) {
            count[arr[i]]++; // 計數每個元素出現次數
        }
        int index = 0;
        for (int i = 0; i < count.length; i++) {
            while (count[i] > 0) {
                arr[index++] = i;
                count[i]--;
            }
        }
    }

    public static void printArray(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }

}

執行結果:

0 1 1 4 5 5 6 7 8 9 

四、擴充套件

前面我們看到,計數排序的前提是輸入元素都是在 [0, k] 這樣的一個區間內。那麼假如我們存在負數的輸入,或者我們也不確定輸入元素的具體範圍是多少,又該怎麼變通呢?

其實,以上情況也不難解決。我們只需要多遍歷一次陣列,找到元素的最大值 max 和最小值 min,然後依舊建立一個長度為 max - min + 1 長度的陣列,其中 min 是可能為負數的。然後我們引入一個變數 offset 來修正數值 i 在計數陣列 count[] 中的正確位置。

程式碼實現:

public class Main {

    public static void main(String[] args) {
        // 輸入元素均在 [0, 10) 這個區間內
        int[] arr = new int[] { -3, 15, -12, 0, 48, 41, -8, 23, 33, 33 };
        countSort(arr);
        printArray(arr);
    }

    public static void countSort(int[] arr) {
        int max = Integer.MIN_VALUE;
        int min = Integer.MAX_VALUE;
        for (int i = 0; i < arr.length; i++) {
            max = Math.max(max, arr[i]);
            min = Math.min(min, arr[i]);
        }
        int offset = 0 - min;
        int[] count = new int[max - min + 1];
        for (int i = 0; i < arr.length; i++) {
            count[arr[i] + offset]++; // 計數每個元素出現次數
        }
        int index = 0;
        for (int i = 0; i < count.length; i++) {
            while (count[i] > 0) {
                arr[index++] = i - offset;
                count[i]--;
            }
        }
    }

    public static void printArray(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }

}

執行結果:

-12 -8 -3 0 15 23 33 33 41 48 

結果也是正確的。

五、總結

可以看到,計數排序是我們目前接觸到的排序演算法中,時間複雜度最低的,僅為 O(n)。

但其也存在一定的限制,它比較適合我們已知輸入元素的取值範圍的情況。