基數排序算法


基數排序Radix sort)是一種非比較型整數排序算法,其原理是將整數按位數切割成不同的數字,然后按每個位數分別比較。由於整數也可以表達字符串(比如名字或日期)和特定格式的浮點數,所以基數排序也不是只能使用於整數。基數排序的發明可以追溯到1887年赫爾曼·何樂禮在打孔卡片制表機(Tabulation Machine)上的貢獻。

它是這樣實現的:將所有待比較數值(正整數)統一為同樣的數位長度,數位較短的數前面補零。然后,從最低位開始,依次進行一次排序。這樣從最低位排序一直到最高位排序完成以后, 數列就變成一個有序序列。

基數排序的方式可以采用LSD(Least significant digital)或MSD(Most significant digital),LSD的排序方式由鍵值的最右邊開始,而MSD則相反,由鍵值的最左邊開始。LSD的基數排序適用於位數小的數列,如果位數多的話,使用MSD的效率會比較好。MSD的方式與LSD相反,是由高位數為基底開始進行分配,但在分配之后並不馬上合並回一個數組中,而是在每個“桶子”中建立“子桶”,將每個桶子中的數值按照下一數位的值分配到“子桶”中。在進行完最低位數的分配后再合並回單一的數組中。

本文地址:http://www.cnblogs.com/archimedes/p/radix-sort-algorithm.html,轉載請注明源地址。

舉例

以LSD為例,假設原來有一串數值如下所示:
73, 22, 93, 43, 55, 14, 28, 65, 39, 81

第一步

首先根據個位數的數值,在走訪數值時將它們分配至編號0到9的桶子中:
0
1    8 1
2    2 2
3    7 3 9 3 4 3
4    1 4
5    5 5 6 5
6
7
8    2 8
9    3 9

第二步

接下來將這些桶子中的數值重新串接起來,成為以下的數列:
81, 22, 73, 93, 43, 14, 55, 65, 28, 39
接着再進行一次分配,這次是根據十位數來分配:
0
1     14
2     22 28
3     39
4     43
5     55
6     65
7     73
8     81
9     93

第三步

接下來將這些桶子中的數值重新串接起來,成為以下的數列:
14,  22,  28,  39,  43,  55,  65,  73,  81,  93
這時候整個數列已經排序完畢; 如果排序的對象有三位數以上,則持續進行以上的動作直至最高位數為止

算法實現

下面的代碼源自維基,原代碼是用C++描述的,這里改為C描述,增加了部分注釋便於理解,由於代碼中有幾處很精妙,很值得學習
// Completed on 2014.10.10 21:10
// Language: C99
//
// 版權所有(C)codingwu   (mail: oskernel@126.com) 
// 博客地址:http://www.cnblogs.com/archimedes/
#include<stdio.h>
#include<stdlib.h>
int maxbit(int data[], int n) //輔助函數,求數據的最大位數
{
    int d = 1; //保存最大的位數
    int p = 10;
    for(int i = 0; i < n; ++i) { while(data[i] >= p) { p *= 10; ++d; } } return d;
}
void radixsort(int data[], int n)   //基數排序
{
    int d = maxbit(data, n);   //數組中的元素的最大位數
    int *tmp = (int *)malloc(n * sizeof(int));
    int *count = (int *)malloc(10 * sizeof(int));   //計數器
    int i, j, k;
    int radix = 1;
    for(i = 1; i <= d; i++) {   //進行d次排序
        for(j = 0; j < 10; j++)
            count[j] = 0;    //每次分配前清空計數器
        for(j = 0; j < n; j++) {
           k = (data[j] / radix) % 10;   //計算每次循環某一位的數字
            count[k]++;    //統計每個桶中的記錄數
        }
        for(j = 1; j < 10; j++)
            count[j] = count[j - 1] + count[j];   //第j個桶以及之前所有桶中元素的總數
        for(j = n - 1; j >= 0; j--) {    //將所有桶中記錄依次收集到tmp中
            k = (data[j] / radix) % 10; tmp[count[k] - 1] = data[j]; count[k]--;
        }
        for(j = 0; j < n; j++)   //將臨時數組的內容復制到data中
            data[j] = tmp[j];
        radix = radix * 10;
    }
    free(tmp);
    free(count);
}
int main()
{
    int a[] = {288, 52, 123, 30, 212, 23, 10, 233};
    int n;
    n = sizeof(a) / sizeof(a[0]);
    radixsort(a, n);
    for(int k = 0; k < n; k++)
        printf("%d ", a[k]);
    printf("\n");
    return 0;
}

效率分析

時間效率:設待排序列為n個記錄,d個關鍵碼,關鍵碼的取值范圍為radix,則進行鏈式基數排序的時間復雜度為O(d(n+radix)),其中,一趟分配時間復雜度為O(n),一趟收集時間復雜度為O(radix),共進行d趟分配和收集。

空間效率:需要2*radix個指向隊列的輔助空間,以及用於靜態鏈表的n個指針。

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM