序言
非比較排序,不需要比較,交換,線上性時間內完成排序。缺點:空間要求較多,不是原地排序,典型的空間換取時間。
計數排序
計數排序利用一個特點:已經排好序(例如從小到大)的數組中,第i個元素為x,則數組中一定有:小於等於x的元素有i個。計數排序需要一個空間代價:n + max_num.
桶排序
桶排序包含兩個步驟:分發元素到每個桶內;順序回收桶內元素。虛擬碼如下:
BUCKET_SORT (A)
- n
← length [A]
- For i = 1 to n do
- Insert A[i] into list
B[nA[i]]
- For i = 0 to n-1 do
- Sort list B with Insertion sort
- Concatenate the lists B[0], B[1], . . B[n-1] together in order.
代碼實現bucket_sort是直接遍曆count 判斷是否大於1,回收元素,counting_sort是把count的內容轉化為
原數組排序後該元素前面元素的個數,同樣需要遍曆一次。
#include <iostream><br />#include <algorithm><br />#include <limits><br />#include <vector><br />#include <ctime><br />#include <cassert></p><p>using namespace std;</p><p>void BucketSort(int *array, int length)<br />{<br />const int MAX = 256;</p><p>int bucket[MAX];</p><p>for (unsigned int i(0); i<MAX; i++) bucket[i] = 0;</p><p>for(unsigned int i=0; i<length; i++)<br />bucket[array[i]]++;</p><p>for(unsigned i=0, idx=0; i<MAX; i++)<br />for(unsigned int k=bucket[i]; k>0; k--)// 回收每個桶內元素<br />array[idx++] = i;<br />}</p><p>/** sort array values, store the results in dst array<br /> @param arr point to source array<br /> @param dst point to destinate array<br /> @param length the array size.<br /> @note Need O( N+Max_Num ) assistant space. space ==> time<br /> **/<br />void counting_sort(int* arr, int* dst, int length)<br />{<br />const int max_num = 255;<br />int count[max_num];</p><p>for (int i(0); i<max_num; i++) count[i] = 0;</p><p>for (int i(0); i<length; i++)<br />count[ arr[i] ]++;</p><p>for (int i(1); i<max_num; i++)<br />count[i] += count[i-1];</p><p>for (int i=length-1; i>=0; i--)<br />{<br />dst[ count[arr[i]]-1 ] = arr[i];// 必須減一<br />count[ arr[i] ]--;<br />}<br />}</p><p>int main ()<br />{<br />#define P_2</p><p>#ifdef P_1<br />{<br />int myints[] = {10,20,30,5,15};<br />int length = sizeof(myints)/sizeof(myints[0]);<br />int* result = new int[length];</p><p>counting_sort(myints, result, length);</p><p>for (int i(0); i<length; i++) cout << result[i] << " ";<br />cout << endl;</p><p>delete result;result = 0;<br />}<br />#endif // P_1</p><p>#ifdef P_2<br />{<br />const int test_num = 10000000;// 1 million<br />srand((int)time(0));</p><p>cout << "Test number " << "/t:/t/t" << test_num << std::endl;</p><p>int* testPtr = new int[test_num];<br />int* testPtr1 = new int[test_num];<br />int* testPtr2 = new int[test_num];</p><p>for(int idx=0; idx<test_num; idx++)<br />testPtr2[idx] = testPtr1[idx] = testPtr[idx] = rand()%255;</p><p>clock_t t = clock();<br />// sort<br />// keep the testPtr1, change the testPtr.<br />counting_sort(testPtr1, testPtr, test_num);<br />cout << "count_sort " << "/t:/t/t" << clock() - t << std::endl;</p><p>t = clock();<br />BucketSort(testPtr1, test_num);<br />cout << "BucketSort " << "/t:/t/t" << clock() - t << std::endl;</p><p>t = clock();<br />std::sort(testPtr2, testPtr2+test_num);<br />cout << "std::sort " << "/t:/t/t" << clock() - t << std::endl;</p><p>// Test the result validity<br />for(int idx=0; idx<test_num; idx++)<br />if (testPtr[idx]!=testPtr1[idx] || testPtr1[idx]!= testPtr2[idx])<br />cout << "wrong";</p><p>delete[] testPtr;testPtr = 0;<br />delete[] testPtr1;testPtr1 = 0;<br />delete[] testPtr2;testPtr2 = 0;<br />}<br />#endif // P_2</p><p>#ifdef P_3<br />#endif // P_3</p><p>system("PAUSE");</p><p>return 0;<br />}
測試結果:
Test number : 10000000<br />count_sort : 112<br />BucketSort : 70<br />std::sort : 4646
計數和桶排序時間上的優勢不用說了,但是這些非比較排序演算法對空間要求比較高,而且跟排序資料的分布也有關。上面的測試結果是基於所有隨機資料都在[0-255]之間的假設前提。資料分布越散亂,空間複雜度越高。
後記
桶排序的思路尤其適合字串數組的排序,因為acsii字元都位於0,255之間。更加一般的桶排序可以使用鏈表,有時間再續吧~