排序演算法之 基數排序 及其時間複雜度和空間複雜度
基數排序(radix sort)屬於“分配式排序”(distribution sort),又稱“桶子法”(bucket sort)或bin sort,顧名思義,它是透過鍵值的部份資訊,將要排序的元素分配至某些“桶”中,藉以達到排序的作用,基數排序法是屬於穩定性的排序,其時間複雜度為O
(nlog(r)m),其中r為所採取的基數,而m為堆數,在某些時候,基數排序法的效率高於其它的穩定性排序法。
演算法分析
主要思想:
基數排序的實現雖然有很多,但是基本思想就是把元素從個位排好序,然後再從十位排好序,,,,一直到元素中最大數的最高位排好序,那麼整個元素就排好序了。
比如:2,22,31,1221,90,85,105
個位排序:90,31,1221,2,22,85,105
十位排序:2,105,1221,22,31,85,90
百位排序:2,22,31,85,90,105,1221
千位排序:2,22,31,85,90,105,1221
注意:每次排序都是在上次排序的基礎上進行排序的,也就是說此次排序的位數上他們相對時,就不移動元素(即順序引數上一個位數的排序順序)
主要步驟:
1、把所有元素都分配到相應的桶中
2、把所有桶中的元素都集合起來放回到陣列中
3、依次迴圈上面兩步,迴圈次數為最大元素最高位數
程式碼分析:
參考下圖
1、豎 0~9:表示桶個數(每個位數上的數字都在0到9之間);
2、行 0~length:0 表示在某個桶內有多少個元素;
3、比如:所有元素中個位為5的有兩個元素,5 , 95;那麼在下圖中存放,分別是:(5,0) = 2;(5,1) = 5;(5,2)= 95;
程式碼實現
#include<stdio.h> #include<stdlib.h> #define LEN 10 // 列印陣列元素 void print_array(int *array, int length) { int index = 0; printf("array:\n"); for(; index < length; index++){ printf(" %d,", *(array+index)); } printf("\n\n"); } // 得到陣列元素中最大數,並且計算其位數個數 void getPosCount(int *array, int length, int *posCount) { int max, index; for (max = *array, index = 0; index < length; index++){ if ( max < *(array + index)) max = *(array + index); } *posCount = 0; while(max){ max = max / 10; (*posCount)++; } } void radixSort(int *array, int length) { int* tmpArray[LEN];// 定義桶個數 0~9 共10個 int index, pos, posCount, element, elementNum, tmp, log = 1; for (element = 0; element < LEN; element++){// 每個桶最大能裝length個元素,預防所有元素都是同一個數 tmpArray[element] = (int*)malloc((sizeof(int))*(length + 1)); tmpArray[element][0] = 0;// 初始化為0 } getPosCount(array, length, &posCount);// 把最高位數存放到posCount中 for (pos = 0; pos < posCount; pos++){// 從個位 ~ 十位 ~ 百位 。。。依次排序 for (element = 0; element < length; element++){// 把元素放到桶裡 分配動作 tmp = ++tmpArray[ (array[element] / log ) % 10][0]; tmpArray[ (array[element] / log) % 10][tmp] = array[element]; } for (index = 0, element = 0; (element < LEN) && (index < length); element++){ for (elementNum = 1; elementNum <= tmpArray[element][0]; elementNum++) array[index++] = tmpArray[element][elementNum]; tmpArray[element][0] = 0; } log = log * 10; } } int main(void) { int array[] = {2, 5, 337, 24, 10000, 5, 30, 123, 3, 9, 100, 1}; //int array[] = {2, 5, 3, 4, 1, 5, 0, 2, 3, 9, 1, 7, 8, 6}; //int array[] = {2, 5, 3, 4, 1, 5, 5, 55555, 9, 5, 7, 5, 6}; int length = (sizeof(array)) / (sizeof(array[1])); print_array(array, length); radixSort(array, length); print_array(array, length); return 0; }
執行結果:
時間複雜度
該演算法所花的時間基本是在把元素分配到桶裡和把元素從桶裡串起來;把元素分配到桶裡:迴圈 length 次; 把元素從桶裡串起來:這個計算有點麻煩,看似兩個迴圈,其實第二迴圈是根據桶裡面的元素而定的,可以表示為:k×buckerCount;其中 k 表示某個桶中的元素個數,buckerCount 則表示存放元素的桶個數; 有幾種特殊情況: 第一、所有的元素都存放在一個桶內:k = length,buckerCount = 1; 第二、所有的元素平均分配到每個桶中:k = length/ bukerCount,buckerCount = 10;(這裡已經固定了10個桶) 所以平均情況下收集部分所花的時間為:length (也就是元素長度 n) 綜上所述: 時間複雜度為:posCount * (length + length) ;其中 posCount 為陣列中最大元素的最高位數;簡化下得:O( k*n ) ;其中k為常數,n為元素個數;空間複雜度
該演算法的空間複雜度就是在分配元素時,使用的桶空間;所以空間複雜度為:O(10 × length)= O (length)計數法基數排序
主要思想:
因為基數排序是根據個位是排序好,然後再根據十位數排序好,依次類推,當最後一個位數排序好時,所有的元素順序都排序好了。又根據計數排序的演算法可以得知,在限制元素的情況下,可以知道計數排序的時間複雜度為 O(n);所有如果利用計數排序的原理的實現基數排序,那麼時間複雜度是不是可以降低呢?程式碼實現
#include<stdio.h>
#include<stdlib.h>
void print_array(int *array, int length)
{
int index = 0;
printf("array:\n");
for(; index < length; index++){
printf(" %d,", *(array+index));
}
printf("\n\n");
}
void getCount(int *array, int length, int *count)
{
int max, index;
for (max = *array, index = 0; index < length; index++){
if ( max < *(array + index)) max = *(array + index);
}
*count = 0;
while(max){
max = max / 10;
(*count)++;
}
}
void radixSort(int *array, int length)
{
int *tmpArray = (int*)malloc(sizeof(int)*(10));
int tmp[length];
int i, j, count, log = 1;
getCount(array, length, &count);
for (j = 0; j < count; j++){ // 迴圈最大位數次
for (i = 0; i < 10; i++)tmpArray[i] = 0;// 初始化陣列
for (i = 0; i < length; i++) tmpArray[ (array[i] / log) % 10 ]++;// 元素值對應桶標記
for (i = 1; i <= 10; i++) tmpArray[i] += tmpArray[i-1];// 統計大於各元素的個數
for (i = length - 1; i >= 0; i--){ // 按照指定位數對元素進行排序
tmp[tmpArray[ (array[i] / log) % 10] - 1] = array[i];
tmpArray[ (array[i] / log) % 10 ]--;
}
for (i = 0; i < length; i++) array[i] = tmp[i];// 把排序好的元素放回到元素陣列中
log = log * 10;
}
free(tmpArray);// 釋放記憶體
}
int main(void)
{
//int array[] = {2, 5, 337, 24, 10000, 5, 30, 123, 3, 9, 100, 1};
int array[] = {2, 5, 3, 4, 1, 5, 0, 2, 3, 9, 1, 7, 8, 6};
int length = (sizeof(array)) / (sizeof(array[1]));
print_array(array, length);
radixSort(array, length);
print_array(array, length);
return 0;
}
執行結果:
時間複雜度
這個時間複雜度比較好計算:count * length;其中 count 為陣列元素最高位數,length為元素個數;所以時間複雜度:O( k*n )
空間複雜度
空間複雜度是使用了兩個臨時的陣列:10 + length;所以空間複雜度:O(n)
若有不正確之處,望大家指正,共同學習!謝謝!!!