基數排序(Radix Sort)

基本思想:

基數排序是一種有意思的排序,在看過其它比較排序后,基數排序真的很有意思。

基數排序(Radix Sort)屬于分配式排序,又稱"桶子法"(Bucket Sort或Bin Sort),將要排序的元素分配到某些"桶"中,以達到排序的作用。基數排序屬于穩定的排序,其時間復雜度為nlog(r)m (其中r為的采取的基數,m為堆數),基數排序的效率有時候高于其它比較性排序。

基數排序的方式可以采用最低位優先LSD(Least sgnificant digital)法最高位優先MSD(Most sgnificant digital)法,LSD的排序方式由鍵值的最右邊開始,而MSD則相反,由鍵值的最左邊開始。LSD的基數排序適用于位數小的數列,如果位數多的話,使用MSD的效率會比較好,MSD的方式恰與LSD相反,是由高位數為基底開始進行分配,其他的演算方式則都相同。

以LSD為例,假設原來有一串數值如下所示:

73, 22, 93, 43, 55, 14, 28, 65, 39, 81 

首先根據個位數的數值,在走訪數值時將它們分配至編號0到9的桶子中:

分配過程:
0 
1 81
2 22
3 73 93 43
4 14
5 55 65
6
7
8 28
9 39 

接下來將這些桶子中的數值重新串接起來,成為以下的數列:

收集過程:
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 

這時候整個數列已經排序完畢;如果排序的對象有三位數以上,則持續進行以上的動作直至最高位數為止。

算法的實現:

#include <stdio.h>
#include <stdlib.h>

#define RADIX_10 10  // 整型排序

/********************************************************
 *函數名稱:print
 *參數說明:array 無序數組
 *        length 數組長度
 *說明:   輸出數組內容
 *********************************************************/
void print(unsigned int array[], int length) {
    for (int j = 0; j < length; j++) {
        printf(" %d ", array[j]);
    }
    printf("\n");
}

/********************************************************
 *函數名稱:getLoopTimes
 *參數說明:num 一個整形數據
 *說明:   返回num的位數
 *********************************************************/
int getLoopTimes(unsigned int num) {
    int count = 1;
    unsigned int temp = num / 10;
    while (temp != 0) {
        count ++;
        temp = temp / 10;
    }
    return count;
}

/********************************************************
 *函數名稱:getMaxNum
 *參數說明:array 代排序的數
 *        length 數組長度
 *說明:   返回最大值
 *********************************************************/
unsigned int getMaxNum(unsigned int array[], int length) {
    unsigned int max = 0;
    for (int i = 0; i < length; i++) {
        if (max < array[i]) {
            max = array[i];
        }
    }
    return max;
}

/********************************************************
 *函數名稱:getNumInPos
 *參數說明:num 一個整形數據
 *          pos 表示要獲得的整形的第pos位數據
 *說明:    找到num的從低到高的第pos位的數據
 *********************************************************/
int getNumInPos(int num, int pos) {
    // 求桶的 index 的除數,如:798
    // 個位桶 index = (798 / 1) % 10 = 8
    // 十位桶 index = (798 / 10) % 10 = 9
    // 百位桶 index = (798 / 100) % 10 = 7
    int temp = 1;
    for (int i = 0; i < pos - 1; i++) {
        temp *= 10;
    }
    return (num / temp) % 10;
}

/********************************************************
 *函數名稱:radixSort
 *參數說明:array 無序數組
 *        length 數組長度
 *說明:   基數排序
 *********************************************************/
void radixSort(unsigned int array[], int length) {
    // 分別為 0~9 的序列空間
    unsigned int *radixArrays[RADIX_10];
    for (int i = 0; i < RADIX_10; i ++) {
        radixArrays[i] = (unsigned int *)malloc(sizeof(unsigned int) * (length + 1));
        radixArrays[i][0] = 0; // 下標為0處元素記錄這組數據的個數
    }
    // 獲取數組中的最大數
    unsigned int maxNum = getMaxNum(array, length);
    // 獲取最大數的位數,也是再分配的次數
    int loopTimes = getLoopTimes(maxNum);
    // 對每一位進行分配
    for (int pos = 1; pos <= loopTimes; pos ++) {
        // 分配過程
        for (int i = 0; i < length; i ++) {
            int num = getNumInPos(array[i], pos);
            int index = ++ radixArrays[num][0];
            radixArrays[num][index] = array[i];
        }
        // 收集過程
        for (int i = 0, j = 0; i < RADIX_10; i ++) {
            for (int k = 1; k <= radixArrays[i][0]; k ++) {
                array[j++] = radixArrays[i][k];
            }
            radixArrays[i][0] = 0; // 復位
        }
        // 輸出數組內容
        print(array, length);
    }
}

int main(int argc, const char * argv[]) {
    unsigned int radixArray[10] = { 73, 22, 93, 43, 55, 14, 28, 65, 39, 81 };
    radixSort(radixArray, 10);
    print(radixArray, 10);
    
    return 0;
}

通過測試,發現程序不對負數進行排序,下面我們實現對負數排序的代碼:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define NULLNUM -1111

// 輸出數組內容
void print(int array[], int length) {
    for (int j = 0; j < length; j++) {
        printf(" %d ", array[j]);
    }
    printf("\n");
}

// a.計算在某一分位上的數據
int pre_process_data(int array[], int length, int weight) {
    int index;
    int value = 1;
    
    for(index = 0; index < weight-1; index++) {
        value *= 10;
    }
    
    for(index = 0; index < length; index ++) {
        array[index] = (array[index] / value) % 10;
    }
    
    for(index = 0; index < length; index ++) {
        if(0 != array[index]) {
            return 1;
        }
    }
    
    return 0;
}

// b.對某一分位上的數據按照[-9~9]排序
void sort_for_basic_number(int array[], int length, int swap[]) {
    int index;
    int basic;
    int total = 0;

    for(basic = -9; basic < 10; basic++) {
        for(index = 0; index < length; index++) {
            if(NULLNUM != array[index] && basic == array[index]) {
                swap[total ++] = array[index];
                array[index] = NULLNUM;
            }
        }
    }
    
    memmove(array, swap, sizeof(int) * length);
}

// c.根據b中的排序結果,對實際的數據進行排序
void sort_data_by_basic_number(int array[], int data[], int swap[], int length, int weight) {
    int index;
    int outer;
    int inner;
    int value = 1;
    
    for(index = 0; index < weight-1; index++) {
        value *= 10;
    }
    
    for(outer = 0; outer < length; outer++) {
        for(inner = 0; inner < length; inner++) {
            if(NULLNUM != array[inner] && data[outer]==((array[inner] / value) % 10)) {
                swap[outer] = array[inner];
                array[inner] = NULLNUM;
                break;
            }
        }
    }
    
    memmove(array, swap, sizeof(int) * length);
    return;
}

// 把a、b、c組合起來構成基數排序,直到某一分位上的數據為0
void radix_sort(int array[], int length) {
    int count;
    int weight = 1;
    int* pData;
    int* swap;
    
    if(NULL == array || 0 == length)
        return;
    
    pData = (int*)malloc(sizeof(int) * length);
    if (NULL == pData) {
        printf("malloc error");
        return;
    }
    memmove(pData, array, length * sizeof(int));
    
    swap = (int*)malloc(sizeof(int) * length);
    if (NULL == swap) {
        printf("malloc error");
        return;
    }
    
    while(1){
        count = pre_process_data(pData, length, weight);
        if(!count) {
            break;
        }
        sort_for_basic_number(pData, length, swap);
        sort_data_by_basic_number(array, pData, swap, length, weight);
        memmove(pData, array, length * sizeof(int));
        weight ++;
    }
    
    free(pData);
    free(swap);
    return;
}

int main(int argc, const char * argv[]) {
    int array[8] = { 49, 38, 65, -7, -110, 10, -20, -49 };
    radix_sort(array, 8);
    print(array,8);
    
    return 0;
}

總結

時間復雜度:
通過上文可知,假設在基數排序中,r為基數,d為位數。則基數排序的時間復雜度為O(d(n+r))。我們可以看出,基數排序的效率和初始序列是否有序沒有關聯。

空間復雜度:
在基數排序過程中,對于任何位數上的基數進行“裝桶”操作時,都需要n+r個臨時空間。

算法穩定性:
在基數排序過程中,每次都是將當前位數上相同數值的元素統一“裝桶”,并不需要交換位置。所以基數排序是穩定的算法。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 概述 排序有內部排序和外部排序,內部排序是數據記錄在內存中進行排序,而外部排序是因排序的數據很大,一次不能容納全部...
    蟻前閱讀 5,214評論 0 52
  • 概述排序有內部排序和外部排序,內部排序是數據記錄在內存中進行排序,而外部排序是因排序的數據很大,一次不能容納全部的...
    Luc_閱讀 2,291評論 0 35
  • 1.插入排序—直接插入排序(Straight Insertion Sort) 基本思想: 將一個記錄插入到已排序好...
    依依玖玥閱讀 1,270評論 0 2
  • 一些概念 該算法是穩定的排序法; 在所有的情況下,時間復雜度均為O(nlog(p)k),空間復雜度為O(n*p)(...
    假裝會編程閱讀 1,052評論 0 0
  • 參加大學同學婚禮的時候,看著新郎新娘互換戒指,突然想起大概一年多前新娘微信上和我聊天時我們的對話---- 新娘:“...
    鱈零kelsey閱讀 973評論 0 1