題外話計數排序時間性能比之前的排序算法高,在實際中應用較多,只需要O(n)時間即可完成排序。計數排序思想比較巧妙,建議大家對照課本多學習,本文主要給出能運行的實例程序。
[C語言必學的12個排序算法:基礎知識 (第0篇)]
線性時間排序
之前學習的快速排序、堆排序、歸并排序都是一類基于比較的排序算法,需要通過比較關鍵字大小確定數據元素的位置。這類算法最優的時間復雜度只能到O(nlogn)。
線性時間排序是一類非比較排序算法,時間復雜度O(n),不需要通過比較關鍵字大小即可完成排序。計數排序(counting sort)是其中一種。
計數排序基本思想
基本思想是給定一個待排的整數序列,對于每一個數據元素,直接存放在保存排序結果的數組對應下標的位置,例如數據元素為5時,直接存放到數組a[5]位置,數據元素為0時,直接存放到數組a[0]的位置。這樣利用待排的數據元素和數組內存地址位置建立一一對應關系,由于數組內存分配是從小到大連續分配,因此最后直接輸出數組,即可獲得有序的整數序列。
計數排序對于輸入的數據元素類型有要求,一般是小范圍的整數或字符,或者很方便利用數據元素本身和內存地址建立意義對應關系。對于n個整數輸入序列,確定每個數據元素取值范圍[0-k],當k值小于等于n時,其時間復雜度是O(n),需要的輔助空間是O(k),當n很小也就是數據量很小,但是n取值范圍很大也就是k值很大時,不適合使用計數排序,因為內存空間浪費嚴重,時間復雜度也變成O(k)。
考慮到待排數據記錄關鍵字有重復,會出現多次,為保證排序結果的穩定性,因此計數排序需要對出現多次關鍵字的數據進行計數,保存到數組對應下標的位置,并且根據該數組計數進一步計算其在排序后的序列的位置。
代碼實現
本實例代碼實現要點:
1.數組a[]保存待排的整數數據記錄,數據記錄本身就是關鍵字,每個整數的取值范圍[0-k],最大取值為k。
2.數組c[]臨時保存每個整數出現的次數,如果沒有出現值為0,利用內存動態分配,大小為k。
3.數組b[]用來臨時保存排序后的整數數據記錄,最終將數組b[]的排序結果復制到數組a[],方便封裝使用。
3.對數組c[]的計數進行累加統計,從而確定每個數據元素在數組b[]的位置,特別是重復出現的數據,在數組b[]中將是一段位置。
/*
#include <stdio.h>
#include <stdlib.h>
void counting_sort(int a[], int length, int k)
{
// 使用calloc會自動初始化為0
// 數組b臨時保存排序后的數據記錄
int *b = (int *)calloc(length, sizeof(int));
// 數組c對數據記錄關鍵字進行計數
int *c = (int *)calloc(k, sizeof(int));
int i;
// 每出現1個數據記錄,對應的數組位置+1
// c[i]表示數據等于i的元素個數
for(i=0; i<length; i++)
c[a[i]] = c[a[i]] + 1;
// 對數組C的計數累累加確定排序后位置
// c[i]表示小于等于i值的數據元素個數
for(i=1; i<k; i++)
c[i] = c[i] + c[i-1];
// 將排序結果輸出到臨時數組b中
for(i=length-1; i>=0; i--)
{
// 注意數組b數組下標從0開始
// 計算的實際位置-1
b[c[a[i]]-1] = a[i];
// 如果數據元素重復出現
// 將該元素下一個保存位置前移
c[a[i]] = c[a[i]] - 1;
}
// 復制到數組a中
for(i=0; i<length; i++)
a[i] = b[i];
free(b);
free(c);
}
int main(void)
{
int a[14] = {4,3,1,2,6,5,0,9,8,7,1,3,0,1};
counting_sort(a, 14, 10);
int i;
for(i=0; i<14; i++)
printf("%d ", a[i]);
return 0;
}
其實做為一個學習者,有一個學習的氛圍跟一個交流圈子特別重要這里我推薦一個C/C++基礎交流583650410,不管你是小白還是轉行人士歡迎入駐,大家一起交流成長。