线性排序:计数排序 Counting Sort 和 基数排序 Radix Sort

て烟熏妆下的殇ゞ 提交于 2020-03-22 00:04:12

3 月,跳不动了?>>>

    基于比较的排序最好的时间复杂度为O(N*lgN),证明如下:

    每种基于比较的排序都能够使用决策树描述其排序过程,每个节点最多有2个子节点。

    该决策树的树叶的最大值即为所有可能的排序结果之和,即N的阶乘N!。

    决策树的高度h即为比较的次数,因为二叉树节点数最多为2^h,所以有N! <= 2^h,根据斯特林公式可知:

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

    因此算法复杂度最好为:

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


    如果要追求效率更高的排序算法,比如线性排序,就要使用其他的非基于比较的排序算法。

    本文用C实现了两种线性排序算法,分别为计数排序Counting Sort 和 基数排序 Radix Sort。这两种算法的实现要求排序元素为整数。

    计数排序包括两步:计数和分配。首先对每个元素出现的次数进行计数,然后设置前缀数组得知每个元素在完成排序的数组中的位置,最后依照前缀数组进行元素分配。

    可以证明,计数排序的时间复杂度为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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!