详谈基数排序
目錄
0. 基數(shù)排序由來
1. 基數(shù)排序圖解
2. 基數(shù)排序代碼
3. 基數(shù)排序測試
4. 小結(jié)
5. 參考資料
0. 基數(shù)排序由來
有一個學(xué)生記錄數(shù)組,數(shù)組元素包含三個字段:系別,班號,班內(nèi)編號。現(xiàn)在要對這個數(shù)組排序,使得系別小的在前,系別相同的班號小的在前,班號相同則班內(nèi)編號較小的在前。這個排序與一般的排序要求(只對某個關(guān)鍵字段排序)不同的是:對多個關(guān)鍵字段排序。有2種方法可解決:一種是掃描一遍記錄,將之按系別分組,再對組內(nèi)記錄按班號分組,然后對組內(nèi)記錄進行單關(guān)鍵字排序;另一種方法是,將這個數(shù)組按班內(nèi)編號排序(班內(nèi)編號在確定的范圍內(nèi)),然后再對整個數(shù)組按班號排序(班號在確定的范圍內(nèi)),最后對整個數(shù)組按系別排序(系別在確定的范圍內(nèi))。第一種方法采取的策略是最高位優(yōu)先排序。第二種方法采取分步驟按關(guān)鍵字類別排序的策略通常稱為基數(shù)排序,可以看出基數(shù)排序的特點是各關(guān)鍵字段的分布是在確定范圍(一般較小)內(nèi)的。
對單關(guān)鍵字排序也可采用多關(guān)鍵字排序的思想:將單關(guān)鍵字分解成多個關(guān)鍵字段,在可控范圍內(nèi),不用比較,直接排序。
計算機實現(xiàn)基數(shù)排序方式:分配和收集。按關(guān)鍵字段將記錄重新分成若各組,再將各個組有序組合成新記錄序列。
1. 基數(shù)排序圖解
1.1 基數(shù)排序圖示
1) 輔助隊列實現(xiàn)基數(shù)排序
不足:需要額外利用隊列來輔助排序,存在空間浪費。
2)鏈隊列實現(xiàn)基數(shù)排序
3)基于桶有序的基數(shù)排序(也稱計數(shù)排序,統(tǒng)計子關(guān)鍵字的出現(xiàn)頻率)
考慮一種簡單情況,如果你給一些unsigned char 排序,除了教科書上的很多方法外,還有一種簡單的。可以這樣考慮,試想排一系列的unsigned char, 值從0~ 255 , 放在 u_char data_array[ARRAY_SIZE] 中,分配一個數(shù)組 int membuffer[256]; 用于統(tǒng)計每個元素出現(xiàn)的次數(shù),也就成為了數(shù)組的排序結(jié)果。
1 for(int i=0;i<256;i++) 2 membuffer[i]=0; 3 for(int i=0;i<ARRAY_SIZE;i++) 4 membuffer[data_array[i]]++;
1.2 基數(shù)排序分析
基數(shù)排序的優(yōu)點就是:不用比較,每個關(guān)鍵字的范圍確定且一般較小,排序時間復(fù)雜度低。但為什么基數(shù)排序的應(yīng)用范圍沒有快速排序那樣廣呢?原因有以下幾點:
1)基數(shù)排序一般需要額外的存儲空間:順序隊列實現(xiàn)需要O(n)的元素空間,鏈隊列實現(xiàn)需要O(n+2d)個指針空間,計數(shù)實現(xiàn)也需要O(n)的元素空間
2)基數(shù)排序的時間復(fù)雜度看似O(n),但它每次排序都是對整個數(shù)組的處理,相對快排來講,cache局部性不好。
在我的印象中,基數(shù)排序一般出現(xiàn)在面試中,在實際生產(chǎn)環(huán)境中還沒有發(fā)現(xiàn)采用基數(shù)排序。
2.基數(shù)排序代碼
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4
5 // 基于無符號整數(shù)的基數(shù)排序
6 void radix(int byte, unsigned int N, const unsigned int *source, unsigned int *dest) {
7 unsigned int count[256];
8 unsigned int index[256];
9 int i = 0;
10
11 memset(count, 0, sizeof (count));
12
13 // 統(tǒng)計出現(xiàn)頻率
14 for (i=0; i < N; ++i) {
15 ++count[((source[i])>>(byte*8))&0xff]; // 以一個字節(jié)為單位
16 }
17
18 // 為dest下標初始化做準備
19 index[0] = 0;
20 for (i = 1; i < 256; ++i) {
21 index[i] = index[i-1] + count[i-1];
22 }
23
24 // 數(shù)組重排列
25 for (i = 0; i < N; ++i) {
26 dest[index[((source[i])>>(byte*8))&0xff]++] = source[i];
27 }
28 }
29
30 void radixsort (unsigned int *source, unsigned int *temp, unsigned int N) {
31 radix(0, N, source, temp);
32 radix(1, N, temp, source);
33 radix(2, N, source, temp);
34 radix(3, N, temp, source);
35 }
36
37
38 int main(int argc, char** argv) {
39 unsigned int a[5] = {300, 200, 2,20, 5};
40 unsigned int len = 5;
41 unsigned int b[5] = {0};
42 unsigned int i = 0;
43
44 radixsort(a, b, len);
45 for (; i < 5; ++i) {
46 printf("%ld ", a[i]);
47 }
48
49 return 0;
50 }
3.基數(shù)排序測試
1)上節(jié)代碼的運行結(jié)果如下:
2)網(wǎng)友的性能對比測試:
引用網(wǎng)絡(luò)上的測試數(shù)據(jù):
今實測性能在n =1024*1000的時候,VC 6 上 release 模式,排序時間:(tick)
n Radix Sort STL Sort
100*1024 20 20
1000*1024 250 320
4000*1024 991 1513
10000*1024 2093 3606
從以上數(shù)據(jù)可見Radix Sort 的確可能比STL Sort 快,而且,因為Radix Sort時間復(fù)雜度小,在更長的時間內(nèi)表現(xiàn)更為充分。n 從100* 1024增長到10000*1024, 排序時間也從20 增長到2093, 可以看出的確是O(n)的。
4. 小結(jié)
基數(shù)排序采用多關(guān)鍵字的排序思想,由最低位到最高位逐步處理各關(guān)鍵字斷序列,達到了O(n)的時間復(fù)雜度,但增加了空間復(fù)雜度。
基數(shù)排序一般適用于已知關(guān)鍵字的分布情況,且分布比較集中。計算機實現(xiàn)一般采用分配-收集策略或計數(shù)方式(統(tǒng)計各關(guān)鍵字的出現(xiàn)頻率)。
5. 參考資料
Radix Sort 的介紹
總結(jié)
- 上一篇: 如何写出好作文如何在电脑上写作文
- 下一篇: 副路由器桥接主路器怎么修改主路由器信道如