基本思想:
基數排序是一種有意思的排序,在看過其它比較排序后,基數排序真的很有意思。
基數排序(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個臨時空間。
算法穩定性:
在基數排序過程中,每次都是將當前位數上相同數值的元素統一“裝桶”,并不需要交換位置。所以基數排序是穩定的算法。