計數排序

計數排序的時間複雜度比快速排序,合併排序(O(nlongn))都要好O(n),但是是以空間代價換取的,並且在範圍較小的整數數中使用。

在介紹技術排序之前先補充時空權衡的思想。

時空權衡

時空權衡的思想就是以空間資源換取時間效率,在算法設計中經常遇到。

需要注意的是:並不是所有的情況下,時間和空間這兩種資源都是相互競爭的。實際上,他們可以聯合起來達到最小化。這種情況需要一個高效的數據結構,如:圖的深度或廣度遍歷算法,在鄰接矩陣中,時間效率是O(n2 );在鄰接鏈表中,時間效率是O(n+e)。無論是時間角度還是空間角度來看,鄰接鏈表的效率更高,在稀疏圖中尤爲明顯。

時空權衡的思想可以分爲以下三點(以及應用算法):

具體思想——>

輸入增強:對問題的部分或全部輸入做預處理,然後將獲得的額外信息進行存儲,以加速後面問題的求解。

預構造:使用額外空間來實現更快和更方便的數據存取。與輸入增強不同,這種技術只涉及到存取結構。

動態規劃:這個策略的基礎是把給定問題中重複子問題的解記錄在表中。


計數排序

計數排序作爲輸入增強的例子,其思路是非常簡單的:針對待排序列表中的每個元素,算出列表中小於該元素的元素個數,並把結果記錄在一張表中。這個“個數”就是該元素在有序列表中的位置。(下面幾種方法的不同就在於這個“個數”是如何得到的)如對某個元素來說,這個個數是10,它應該排在有序數組第11個位置(如果從0開始計數,則下標是10)。

幾個計數排序的比較——>

  • 通用的計數排序:時間O(n);空間O(n+k),k是最大元素值
  • 比較計數排序:時間O(n2 );空間O(n)
  • 分佈計數法排序:時間O(n)

通用的計數排序

  • 列表元素個數爲n,最大元素值爲k
  • 開闢數組count[k]用於記錄元素的個數
  • 開闢數組temp[n]用於存儲有限列表
  • 場合:適用於個數較多,但元素大小範圍較小的整數列表

代碼——>

public class GeneralCountingSort {

    public static void main(String[] args) {
        test();
    }
    /**
     * 
     * @param array正整型待排序數組
     * @return 
     */
    public static int[] conutsorting(int[] array) {
        //數組長度
        int n = array.length;
        //元素最大值,數組範圍
        int k = 0;
        for (int i = 0; i < n; i++) {
            k = array[i] > k ? array[i] : k;
        }
        //記錄元素個數
        int[] count = new int[k+1];
        for (int i = 0; i < n; i++) {
            count[array[i]]++;
        }
        //元素按個數累加得到序號,即小於該元素的“個數”
        for (int i = 1; i <= k; i++) {
            count[i] += count[i-1];
        }
        //排序
        int[] temp = new int[n];
        for (int i = 0; i < n; i++) {
            count[array[i]]--;//①相同元素放置②下標從0開始
            temp[count[array[i]]] = array[i];
        }
        return temp;
    }

    /**
     * 測試
     */
    public static void test() {
        int[] array = {7, 4, 2, 1, 5, 3, 1, 5};
        int[] result = conutsorting(array);
        for (int i = 0; i < result.length; i++) {
            System.out.print(result[i] + " ");
        }
    }
}

比較計數排序

比較計數排序開闢空間爲O(n),所以這裏的計數方式與通用計數排序不同,仍要依次比較後才記錄,時間複雜度爲O(n2 )。

例子——>

數組A[0..5]: 62 31 84 96 19 47
初始記錄數組count[]: 0 0 0 0 0 0
i=0時 3 0 1 1 0 0
i=1時 1 2 2 0 1
i=2時 4 3 0 1
i=3時 5 0 1
i=4時 0 2
最終狀態 3 1 4 5 0 2
數組S[0..5]: 19 31 47 62 84 96

僞代碼——>

ComparisonCountingSort(A[0..n-1])
//用比較計數法對數組排序
//輸入:待排序數組A[0..n-1]
//輸出:將A中的元素按升序排列的數組S0..n-1]
for(i = 0; i <= n-1; i++) do 
  Count[i] ← 0;
for(i = 0; i <= n-2; i++) do
  for(j = i+1; j <= n-1; i++) do
     if(A[i] < A[j])
        Count[j] ← Count[i] + 1;
     else   
        Count[i] ← Count[i] + 1;
for(i = 0; i <= n-1; i++) do
    S[Count[i]A[i]];
return S;

分佈計數法排序

思想:如果帶排序的元素的值都來自於一個已知的小範圍,即元素的值位於下界l和上界u之間的整數,可以計算元素出現的頻率,記錄在F[0..u-l]中,這樣前F[0]個位置填入l,接着F[1]個位置填入l+1,以此類推。

例子——>

待排序數組:13 11 12 13 12 12

數組值 11 12 13
頻率 1 3 2
分佈值 1 4 6

處理過程:D[0..2]表示分佈值,從右往左更容易(即從12開始)

________      ___D[0..2]___    ___S[0..5]___
A[5] = 12     1     4*    6    - - - 12 - -
A[4] = 12     1     3*    6    - - 12 - - -
A[3] = 13     1     2     6*   - - - - - 13
A[2] = 12     1     2*    5    - 12 - - - -
A[1] = 11     1*    1     5    11 - - - - -
A[0] = 13     0     1     5*   - - - - 13 -

僞代碼——>

DistributionCountingSort(A[0..n-1],l,u)
//分佈計數法,對來自於有限範圍整數的一個數組進行排序
//輸入:數組A[0..n-1],數組中的整數位於l和u之間(l<=u)
//輸出:A中元素構成的非降序數組S[0..n-1]
for(j = 0; j <= u-l; j++) do D[j] ← 0;//初始化頻率數組
for(i = 0; i <= n-1; i++) do D[A[i]-l] ← D[A[i]-l] + 1;//計算頻率值
for(j = 0; j <= u-l; j++) do D[j] ← D[j-1] + D[j];//重用於分佈
for(i = n-1; i >= 0; i--) do
  j ←A[i]-l;
  S[D[j]-1] ← A[i];
  D[j] ← D[j] - 1;//分佈值減1,得到下個相同元素
return S;

參考:

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