【1】桶排序
桶排序(也稱箱排序),據坊間演繹,其實現方式有很多。
在此我們僅僅闡述一下本文的實現思想,以便於更好的理解下面的內容,同時加深對桶排序的認識。
首先,說明一點,我們是使用數組模擬桶(最好應該是使用鏈表模擬)。
所謂數組模擬桶實現排序的過程到底是怎么進行的呢?呵呵!其實還真有點抽象。
實現步驟如下:
(1)定義映射函數
<1>求得欲排數據序列中的最大數據。
<2>通過遍歷欲排數據對每個數據乘以10再與最大數據取余,求得每個數據對應桶的索引(或稱關鍵字)。
(2)求得每個桶中盛放的數據個數(為了保證隨后准確分配數據)
(3)求得每個桶盛放數據個數的右邊界索引(所謂的桶邏輯控制)
(4)從右向左(確保穩定性)掃描欲排數據序列,依次分配到對應桶中
(5)對各桶中所盛數據進行收集
(6)利用插入排序再對各個桶中所盛數據進行排序
(7)直至排序結束,即為已排序數據序列
其實,整個過程再講通俗一點,可以如下描述:
建立一個數組作為我們的所謂的桶(邏輯桶)
然后,申請開辟與欲排數據所占空間相同的內存,作為真正的盛放數據的桶(物理桶)
數組的索引默認為桶號
對數組的每一次賦值都有着不同的意義(請參考代碼分析)
最后再用插入排序對各桶中所收集數據分別進行排序。即完成桶排序。
總而言之:先分類,后收集,再排序。
【2】示例代碼及其分析過程
(1)代碼如下:
1 #include<iostream>
2 #include<malloc.h>
3 using namespace std; 4
5
6 void PrintArr(int ar[],int n) 7 { 8 for(int i = 0; i < n; ++i) 9 cout<<ar[i]<<" "; 10 cout<<endl; 11 } 12
13 int MapToIndex(int x,int max) 14 { 15 return (10 * x) / max; 16 } 17
18 void insertion_sort(int arr[],int begin,int end) 19 { 20 for(int i = begin+1; i <= end; ++i) 21 { 22 int v = arr[i]; 23 int j = i; 24 while(j-1 >= begin && v<arr[j-1]) 25 { 26 arr[j--] = arr[j-1]; 27 } 28 arr[j] = v; 29 } 30 } 31
32 void bucket_sort(int ar[], int begin, int end) 33 { 34
35 const int radix = 11 ; //注意:為什么是11?
36 int count[radix], i, j; 37 int size = end-begin+1; 38
39 //計數值置空
40 for(i = 0; i < radix; ++i) 41 { 42 count[i] = 0; //置空
43 } 44
45 //end-begin+1 = 9 - 0 + 1 = 10
46 int *Temp = (int *) malloc((size) * sizeof(int)); //分配臨時空間 47
48 //取得當前待排序數據中的最大數據
49 int max = 0; 50 for(i = begin; i < size; ++i) 51 { 52 if(ar[i] > max) 53 max = ar[i]; 54 } 55
56 //統計各桶需要裝的元素的個數
57 for(i = begin; i < size; ++i) 58 { 59 count[MapToIndex(ar[i], max)]++; 60 } 61
62 //輸出計數結果:
63 PrintArr(count, radix); 64
65 //求出桶的邊界索引,count[i]為第i個桶的右邊界索引+1
66 for(i = 1; i < radix; ++i) 67 { 68 count[i] = count[i] + count[i-1]; 69 } 70
71 //輸出桶邊界索引
72 PrintArr(count, radix); 73
74 //從右向左掃描,保證排序穩定性
75 for(i = end; i >= begin; --i) 76 { 77 j = MapToIndex(ar[i], max); 78 Temp[count[j]-1] = ar[i]; //放入對應的空間中,count[j]-1是第j個桶的右邊界索引
79 --count[j]; //准備放置下一個元素的位置索引
80 } 81
82 for(int i = 0; i < size; ++i) 83 { 84 cout<<Temp[i]<<" "; 85 } 86 cout<<endl; 87
88 PrintArr(count, radix); 89
90 //從各個桶中收集數據
91 for(i = begin, j = 0; i < size; i++, j++) 92 { 93 ar[i] = Temp[j]; 94 } 95
96 PrintArr(ar, end+1); 97
98 //釋放空間
99 free(Temp); 100
101 for(i = 0; i < size; i++) 102 { 103 int index1 = begin + count[i]; //得到第i個桶的左邊界
104 int index2 = begin + count[i+1] - 1; //得到第i個桶的右邊界
105 if(index1 < index2) 106 insertion_sort(ar, index1, index2); 107 } 108 } 109
110 void main() 111 { 112 int ar[] = {12, 14, 54, 5, 6, 3, 9, 8, 47, 89}; 113 int len = sizeof(ar)/sizeof(int); 114 bucket_sort(ar, 0, len-1); 115 PrintArr(ar, len); 116 } 117 /*
118 4 3 0 0 0 1 1 0 0 0 1 119 4 7 7 7 7 8 9 9 9 9 10 120 5 6 3 8 12 14 9 47 54 89 121 0 4 7 7 7 7 8 9 9 9 9 122 5 6 3 8 12 14 9 47 54 89 123 3 5 6 8 9 12 14 47 54 89 124 */
(2)分析過程如下:
學過數據結構的估計都可以想象得到:桶排序中所謂的桶應該是用一個單鏈表實現
因為,我們一直覺得,計算機世界就是對現實世界的模擬。那么,既然是山寨,當然越逼真越好
但是,假如我們沒有學習過單鏈表,腦子里面根本沒有單鏈表的概念。而且,我們要實現桶排序。
好吧!我唯一可以借助的就是數組。
不過數組模擬桶實現桶排序的確不是很好理解,有幾分抽象
上面這就是一個用數組模擬桶實現的桶排序示例代碼,現在做一下具體分析,希望可以更深刻理解桶排序。
分析過程如下:
(1)待排序數據序列為:
(2)count數組本質上是邏輯的桶,為什么說是邏輯上的桶?因為,它控制着桶的所有數據邏輯
但是申請的內存Temp才是每個桶的真正儲存空間,所以只能說是邏輯上的桶。
而當數據被分配到各個桶中又從桶(即就是從申請空間)被收集之后,就對申請空間進行釋放(因為留着再沒有必要)。
如何理解以上內容?請結合一下圖示理解具體分析:
第一行:所建桶的索引(可以看到總共為11個桶)
為什么是11個桶?由我們在對待排數據求輸入桶對應的索引時匹配函數(MapToIndex)決定。
由於最大數據輸入匹配函數后所得桶對應索引為10。所以,在此必須如此設計。
第二行:所有桶置空
第三行:每個桶中待盛的數據個數
第四行:每個桶的右邊界索引
如何理解右邊界索引?
比如0號桶,第三行已經得出其待盛四個數據,那么將來數據就會存入Temp[0],Temp[1],Temp[2],Temp[3]
比如1號桶,第三行已經得出其待盛三個數據,那么將來數據就會存入Temp[4],Temp[5],Temp[6]
第五行:待排數據全部放入各個桶中后,桶的左邊界索引(這個是為了下面插入排序使用)。
(3)對每一個桶(即就是申請空間)中所盛的數據再利用插入排序進行排序
(4)數組中的數據即為已排序序列
【3】桶排序分析
桶排序是穩定排序算法。
桶排序使用范圍比較窄。
Good Good Study, Day Day Up.
順序 選擇 循環 堅持 總結