線性排序:計數排序 Counting Sort 和 基數排序 Radix Sort

    基於比較的排序最好的時間複雜度爲O(N*lgN),證實以下: git

    每種基於比較的排序都可以使用決策樹描述其排序過程,每一個節點最多有2個子節點。 算法

    該決策樹的樹葉的最大值即爲全部可能的排序結果之和,即N的階乘N!。 shell

    決策樹的高度h即爲比較的次數,由於二叉樹節點數最多爲2^h,因此有N! <= 2^h,根據斯特林公式可知: ubuntu

    h >= lgN! >= lg(N/e)^N = N*lg(N/e) = N*lgN - N*lge 數組

    所以算法複雜度最好爲: 測試

    O(N*lgN - N*lge) = O(N*lgN) spa


    若是要追求效率更高的排序算法,好比線性排序,就要使用其餘的非基於比較的排序算法。 code

    本文用C實現了兩種線性排序算法,分別爲計數排序Counting Sort 和 基數排序 Radix Sort。這兩種算法的實現要求排序元素爲整數。 排序

    計數排序包括兩步:計數和分配。首先對每一個元素出現的次數進行計數,而後設置前綴數組得知每一個元素在完成排序的數組中的位置,最後依照前綴數組進行元素分配。 it

    能夠證實,計數排序的時間複雜度爲O(k+n),其中k爲元素最大值,n爲元素個數。

    計數排序簡單實現以下:

/* Counting Sort include two steps: 
 * Countint and Distribution. 
 */
void countingSort(int arr[], size_t nmeb)
{
    int i;
    int max;
    int countArr[max];
    int prefixArr[max];
    int holdArr[nmeb];
    
    max = arr[0];
    for (i = 1; i < nmeb; i++) {
        if (max < arr[i])
            max = arr[i];
    }

    /* initialize countint array */
    for (i = 0; i < max; i++)
        countArr[i] = 0;

    /* step 1: counting */
    for (i = 0; i < nmeb; i++)
        countArr[arr[i]]++;

    /* bulid prefix array */
    prefixArr[0] = countArr[0];
    for (i = 1; i < max; i++)
        prefixArr[i] = countArr[i] + prefixArr[i - 1];

    /* step 2: distribution */
    for (i = nmeb - 1; i >= 0; i--) {
        holdArr[prefixArr[arr[i]] - 1] = arr[i];
        prefixArr[arr[i]]--;
    }
        
    /* copy array */
    for (i = 0; i < nmeb; i++) 
        arr[i] = holdArr[i];
}


    基數排序在計數排序的基礎上做出了改進。其思想是首先把數組元素按照必定位數(bits)分爲多層數字(digits),而後從低層到高層,分別按照每層數字(digits)進行計數排序。

    好比, 對 (132, 321, 123)進行排序,按照10進制進行分層。

    按照個位排序: 321,132,123

    按照十位排序: 321,123,132

    按照百位排序: 123,132,321

    排序完成。

    然而,不可以僅僅按照10或者2做爲位數進行分層,這會形成大量的分層,以致於要耗費屢次計數排序。

假設數組的最大值爲k,並且2^b>=k 且 2^(b-1)<=k,咱們要算出最優的分層位數r(二進制)。

    可知,時間的複雜度爲所分層數b/r與計數排序時間複雜度O(n+k)的乘積,即

    O(b/r*(n+k)) = O(b/r*(n+2^b))

    經過求導或者其餘方法可知r=lgn時,值最優。

    按照上面所述,基數排序的實現以下:

void countingSortBits(int arr[], size_t nmeb, int bits, int digits)
{
    int i, max;
    max = 1 << bits;
    int countArr[max];
    int prefixArr[max];
    int holdArr[nmeb];
    int copyArr[nmeb];

    /* copy array hold one digits of original array */
    for (i = 0; i < nmeb; i++)
        copyArr[i] = arr[i] % (1 << ((digits + 1) * bits)) / (1 << (digits * bits));


    /* initialize countint array */
    for (i = 0; i < max; i++)
        countArr[i] = 0;

    /* step 1: counting */
    for (i = 0; i < nmeb; i++)
        countArr[copyArr[i]]++;

    /* bulid prefix array */
    prefixArr[0] = countArr[0];
    for (i = 1; i < max; i++)
        prefixArr[i] = countArr[i] + prefixArr[i - 1];

    /* step 2: distribution */
    for (i = nmeb - 1; i >= 0; i--) {
        holdArr[prefixArr[copyArr[i]] - 1] = arr[i];
        prefixArr[copyArr[i]]--;
    }
        
    /* copy array */
    for (i = 0; i < nmeb; i++) 
        arr[i] = holdArr[i];
}

int radixSort(int arr[], size_t nmeb)
{
    int i, r, k, max;
    max = arr[0];
    for (i = 1; i < nmeb; i++) {
        if (max < arr[i])
            max = arr[i];
    }

    for (r = 1; (nmeb >> r) > 0; r++)
        ;

    for (k = 1; (max >> k) > 0; k++)
        ;

    for (i = 0; i * r < k; i++)
        countingSortBits(arr, nmeb, r, i);
}


簡單測試:

int main()
{
    int i;
    int arr[10] = {89767, 12389, 13289, 12833, 11289, 87263, 48928, 12932, 32674, 65323};
    printf("Original:\n");
    for (i = 0; i < 10; i++)
        printf("%6d", arr[i]);
    printf("\n");

    radixSort(arr, 10);

    printf("Sorted:\n");
    for (i = 0; i < 10; i++)
        printf("%6d", arr[i]);
    printf("\n");
    return 0;
}

執行結果以下:

roo@ubuntu:~$ ./a.out 
Original:
 89767 12389 13289 12833 11289 87263 48928 12932 32674 65323
Sorted:
 11289 12389 12833 12932 13289 32674 48928 65323 87263 89767
相關文章
相關標籤/搜索