1. 程式人生 > >基數排序(C語言版)

基數排序(C語言版)

先說說基數排序的思想

基數排序是非比較型的排序演算法,其原理是將整數按位數切割成不同的數字,然後按每個位數分別比較。

將所有待比較數值(正整數)統一為同樣的數位長度,數位較短的數前面補零。然後,從最低位開始,依次進行一次排序。在每一次排序中,按照當前位把陣列元素放到對應的桶當中,然後把桶0到桶9中的元素按先進先出的方式放回陣列中。這樣從最低位排序一直到最高位排序完成以後, 數列就變成一個有序序列。

穩定性和複雜度:

基數排序是穩定的排序演算法;時間複雜度為O(k×n),空間複雜度為O(n),其中k為陣列最大數的位數,n為陣列元素的個數。

基數排序的方式可以採用LSD(Least significant digital)或MSD(Most significant digital),LSD的排序方式由鍵值的最右邊開始,而MSD則相反,由鍵值的最左邊開始。LSD適用於陣列中元素位數不多的情況,而MSD則剛好相反,適用於陣列中元素位數較多的情況。

我在這裡是按LSD的方式編寫的程式碼。

下面是我用C語言實現的基數排序,是鏈式基數排序演算法,如果有什麼錯誤請大家指出,謝謝。

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

#define RADIXCOUNT 10  //桶的個數,桶號:0 1 2 ..... 9
#define RANDMAX 100000 //隨機數的最大值加1+

struct Node {
    int value;
    struct Node *next;
};

struct Queue {
    struct Node *head;
    struct Node *tail;
};

void getRandArray(int array[], int size);
void radixSort(int array[], int size);
void printArray(int array[], int size);
int getMaxLength(int array[], int size);
void distributeNumbers(int array[], int size, struct Queue bucket[], int dividend);
void rearrangeArray(int array[], int size, struct Queue bucket[]);
void isSorted(int array[], int size);

int main(int argc, char const *argv[])
{
    int size = 0;
    scanf("%d", &size);
    assert(size > 0);

    int *array = (int *)calloc(size, sizeof(int));
    getRandArray(array, size);
    printArray(array, size);

    radixSort(array, size);
    printArray(array, size);
    isSorted(array, size);

    free(array);
    return 0;
}

//利用偽隨機數填充陣列array
void getRandArray(int array[], int size)
{
    assert(array != NULL && size > 0);

    srand((unsigned) time(NULL));
    int i = 0;
    for (i = 0; i < size; ++i) {
        //產生RANDMAX以內的偽隨機數
        array[i] = rand() % RANDMAX;
    }
}

//基數排序,按從小到大的順序進行排列
void radixSort(int array[], int size)
{
    assert(array != NULL && size > 0);

    struct Queue bucket[RADIXCOUNT];
    int i = 0;
    for (i = 0; i < RADIXCOUNT; ++i) {
        bucket[i].head = NULL;
        bucket[i].tail = NULL;
    }

    int maxLength = getMaxLength(array, size);
    int dividend = 1;
    for (i = 0; i < maxLength; ++i) {
        distributeNumbers(array, size, bucket, dividend);
        rearrangeArray(array, size, bucket);
        dividend *= 10;
        // printArray(array, size);
    }
}

//獲取陣列array中最大數的長度(位數)
int getMaxLength(int array[], int size)
{
    assert(array != NULL && size > 0);

    int max = array[0];
    int i = 0;
    for (i = 1; i < size; ++i) {
        if (max < array[i]) {
            max = array[i];
        }
    }

    int length = 1;
    while ((max /= 10) != 0) {
        ++length;
    }
    return length;
}

//把陣列array中的數放到對應的桶中,桶的底層是用鏈式佇列實現的
void distributeNumbers(int array[], int size, struct Queue bucket[], int dividend)
{
    assert(array != NULL && size > 0 && bucket != NULL && dividend > 0);

    int radixValue = 0;
    struct Node *node;
    int i = 0;
    for (i = 0; i < size; ++i) {
        //把array[i]放到下標為radixValue的桶中
        radixValue = (array[i] / dividend) % 10;
        node = (struct Node *) malloc(sizeof(struct Node));
        node -> value = array[i];
        node -> next = NULL;
        if (bucket[radixValue].head == NULL) {
            bucket[radixValue].head = node;
            bucket[radixValue].tail = node;
        } else {
            bucket[radixValue].tail -> next = node;
            bucket[radixValue].tail = node;
        }
    }
}

//把桶0..9中的數按放入桶中的先後次序放回到陣列array中
void rearrangeArray(int array[], int size, struct Queue bucket[])
{
    assert(array != NULL && size > 0 && bucket != NULL);

    struct Node *pointer = NULL;
    int arrayIndex = 0;
    int listIndex = 0;
    for (listIndex = 0; listIndex < RADIXCOUNT; ++listIndex) {
        while (bucket[listIndex].head != NULL) {
            array[arrayIndex++] = bucket[listIndex].head -> value;
            pointer = bucket[listIndex].head;
            bucket[listIndex].head = bucket[listIndex].head -> next;
            free(pointer);
        }
    }
}

void printArray(int array[], int size)
{
    assert(array != NULL && size > 0);

    int i = 0;
    for (i = 0; i < size; ++i) {
        printf("%d ", array[i]);
    }
    printf("\n\n");
}

//判斷陣列array是否已經是有序的
void isSorted(int array[], int size)
{
    assert(array != NULL && size > 0);

    int unsorted = 0;
    int i = 0;
    for (i = 1; i < size; ++i) {
        if (array[i] < array[i - 1]) {
            unsorted = 1;
            break;
        }
    }

    if (unsorted) {
        printf("the array is unsorted!\n");
    } else {
        printf("the array is sorted!\n");
    }
}

由於多寫了判斷陣列是否有序的子程式和生成偽隨機陣列的子程式,還有就是因為要充分利用記憶體所以我用了連結串列來充當桶的角色,綜上所述會讓程式顯得比較臃腫,不過核心演算法部分還是比較簡練的,希望大家能指出我的程式中錯誤和不足之處。

       我在網上還見過一種利用陣列來充當桶的角色的程式,利用陣列充當桶可以讓程式碼十分簡潔,效率也特別高,但是這種方法有一個致命的弱點就是待排序的陣列大小不能太大,否則充當桶的陣列將會過於龐大(由於單個桶的大小需要和陣列的大小一致,而每個桶都放不滿),空間複雜度達到O(10*n),空間利用率太低。

其他八種排序演算法的部落格: