計數排序可能是最簡單的一種排序,它可以被用來對一個列表進行排序,並且是基數排序的一個關鍵部分。這兩者都是被Harold Seward發明的,這篇文章我將解釋計數排序並且用C寫出來。
- 計數排序
計數排序非常基礎,他的主要目的是對整數排序並且會比普通的排序演算法效能更好。例如,輸入{1, 3, 5, 2, 1, 4}給計數排序,會輸出{1, 1, 2, 3, 4, 5}。這個演算法由以下步驟組成:
- 初始化一個計數陣列,大小是輸入陣列中的最大的數。
- 遍歷輸入陣列,遇到一個數就在計數陣列對應的位置上加一。例如:遇到5,就將計數陣列第五個位置的數加一。
- 把計數陣列直接覆蓋到輸出陣列(節約空間)。
- 例子
輸入{3, 4, 3, 2, 1},最大是4,陣列長度是5。
建立計數陣列{0, 0, 0, 0}。
遍歷輸入陣列:
{3, 4, 3, 2, 1} -> {0, 0, 1, 0}
{3, 4, 3, 2, 1} -> {0, 0, 1, 1}
{3, 4, 3, 2, 1} -> {0, 0, 2, 1}
{3, 4, 3, 2, 1} -> {0, 1, 2, 1}
{3, 4, 3, 2, 1} -> {1, 1, 2, 1}
計數陣列現在是{1, 1, 2, 1},我們現在把它寫回到輸入陣列裡:
{0, 1, 2, 1} -> {1, 4, 3, 2, 1}
{o, o, 2, 1} -> {1, 2, 3, 2, 1}
{o, o, 1, 1} -> {1, 2, 3, 2, 1}
{o, o, o, 1} -> {1, 2, 3, 3, 1}
{o, o, o, o} -> {1, 2, 3, 3, 4}
這樣就排好序了。
- 時間:O(n + k),n是輸入陣列長度,k是最大的數的大小。
- 空間:O(n + k),n是輸入陣列長度,k是最大的數的大小。
- 程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
#include <stdio.h> #include <stdlib.h> void printArray(int * array, int size){ int curr; for(curr = 0; curr < size; curr++){ printf("%d, ", array[curr]); } printf("\n"); } int maximum(int * array, int size){ int curr = 0; int max = 0; for(curr = 0; curr < size; curr++){ if(array[curr] > max){ max = array[curr]; } } return max; } void countingSort(int * array, int size){ int curr = 0; int max = maximum(array, size); int * counting_array = calloc(max, sizeof(int)); // Zeros out the array for(curr = 0; curr < size; curr ++){ counting_array[array[curr]]++; } int num = 0; curr = 0; while(curr <= size){ while(counting_array[num] > 0){ array[curr] = num; counting_array[num]--; curr++; if(curr > size){ break; } } num++; } printArray(array, size); } int main(){ int test1[] = {2, 6, 4, 3, 2, 3, 4, 6, 3, 4, 3, 5, 2, 6}; int size1 = 14; countingSort(test1, size1); int test2[] = {5, 6, 7, 8, 5}; int size2 = 5; countingSort(test2, size2); int test3[] = {8, 1, 2, 3, 3, 4}; int size3 = 6; countingSort(test3, size3); return 0; } |
插播一句:如果你程式設計有困難,無妨看看我的教程。
- 總結
不幸的是,這個演算法的簡潔性同時也是它的弱點。很多程式設計師不需要對整數排序,至少他們覺得他們不需要。其實通常非整數都可以被規約為整數,然後再用計數排序或者基數排序(基數排序就是多加了一層,這樣會快一些)。谷歌一下可以有不少結果,比如這篇文章。