01 箱子排序
1.1 什么是分配排序?
分配排序的基本思想:排序過程無須比較關鍵字,而是通過"分配"和"收集"過程來實現排序.它們的時間復雜度可達到線性階:O(n)。
1.2 什么是箱子排序?
箱子排序是分配排序的一種,箱子排序也稱桶排序(Bucket Sort),其基本思想是:設置若干個箱子,依次掃描待排序的記錄 R[0],R[1],…,R[n-1],把關鍵字等於 k 的記錄全都裝入到第 k 個箱子里(分配),然后按序號依次將各非空的箱子首尾連接起來(收集)。
比如,要將一個班的同學按分數排序,分數范圍是0-100分。需設置 101 個"箱子"(R[0],R[1],…,R[100]),排序時依次將每個同學按分數放入相應的箱子里,然后依次將這些箱子首尾相接,就得到了按分數遞增序排列的一個班的同學。
1.3 關於箱子個數
箱排序中,箱子的個數取決於關鍵字的取值范圍。
若關鍵字的取值范圍是0到m-1的整數,則必須設置 m 個箱子。因此箱排序要求關鍵字的類型是有限類型,否則可能要無限個箱子。
02 鏈表實現箱子排序
一般情況下每個箱子中存放多少個關鍵字相同的記錄是無法預料的,故箱子的類型應設計成鏈表為宜。
我們現在來講解一個簡單的例子,以便來讓大家更好了解這個過程。
2.1 example
下面是一個學生鏈表。為了更好說明問題,我們簡化了學生的存儲結構。每個學生節點保存一個字符,表示學生的姓名,再存一個數字,表示學生的分數。分數范圍為0-5。

2.2 箱子排序的步驟
有了上面的輸入鏈表以后。我們采用以下步驟進行箱子排序:
1) 逐個刪除輸入鏈表的節點,然后把刪除的節點分配到相應的箱子中。
2) 把每個箱子中的元素收集並鏈接起來,使其成為一個有序鏈表。
比如上面的輸入鏈表,我們要做的是:
1) 連續刪除鏈表的首元素,並將其插入到相對應箱子的鏈表頭部。
2) 從最后一個箱子開始,逐個刪除每個箱子的元素,並將其插入一個初始為空的鏈表的頭部。
如下圖所示:

那么排序好的鏈表如下:

03 動手寫代碼
3.1 studentRecord結構體
先來看看代碼:
1struct studentRecord
2{
3 int score;
4 string name;
5
6 studentRecord() {}
7 studentRecord(int theScore, string theName) :score(theScore), name(theName) {}
8
9 int operator != (const studentRecord & x) const
10 {
11 return (score != x.score);
12 }
13 operator int() const { return score; }
14};
在studentRecord這個結構體里面,我們重載了 != 這個運算符,以便用於比較等操作。還重載了int()運算符,這樣一來,借助int()轉換符就可以直接對學生結構體進行+-*/等操作了。
3.2 箱子排序代碼
還是先看看代碼吧。
1void binSort(chain<studentRecord> & theChain, int range)
2{
3 chain<studentRecord> * bin = new chain<studentRecord>[range + 1]; // 0 to range
4 int numberOfElements = theChain.size();
5
6 for (int i = 0; i < numberOfElements; i++)
7 {
8 studentRecord record = theChain.get(0);
9 theChain.erase(0);
10
11 bin[record.score].insert(0, record);
12 }
13 for (int j = range; j >= 0; j--)
14 {
15 while (!bin[j].empty())
16 {
17 studentRecord record = bin[j].get(0);
18 bin[j].erase(0);
19 theChain.insert(0, record);
20 }
21 }
22
23 delete[] bin;
24}
該函數只有兩個參數,一個是學生鏈表。還有一個是排序范圍(設置為0~range)。函數主體就是按部就班的進行上面所說的兩步操作了。這里的chain鏈表是事先封裝好的一個類。
04 完整代碼
貼上一個完整的代碼:
1#include <iostream>
2#include <string>
3#include <time.h>
4#include <stdlib.h>
5#include "../03_線性表_鏈式描述/chain.h"
6#include "../03_線性表_鏈式描述/chain.cpp"
7
8using std::cout;
9using std::cin;
10using std::endl;
11using std::string;
12
13struct studentRecord
14{
15 int score;
16 string name;
17
18 studentRecord() {}
19 studentRecord(int theScore, string theName) :score(theScore), name(theName) {}
20
21 int operator != (const studentRecord & x) const
22 {
23 return (score != x.score);
24 }
25 operator int() const { return score; }
26};
27
28//override out
29ostream & operator<<(ostream & out, const studentRecord & x)
30{
31 out << x.name << " " << x.score << endl;
32 return out;
33}
34
35void binSort(chain<studentRecord> & theChain, int range)
36{
37 chain<studentRecord> * bin = new chain<studentRecord>[range + 1]; // 0 to range
38 int numberOfElements = theChain.size();
39
40 for (int i = 0; i < numberOfElements; i++)
41 {
42 studentRecord record = theChain.get(0);
43 theChain.erase(0);
44
45 bin[record.score].insert(0, record);
46 }
47 for (int j = range; j >= 0; j--)
48 {
49 while (!bin[j].empty())
50 {
51 studentRecord record = bin[j].get(0);
52 bin[j].erase(0);
53 theChain.insert(0, record);
54 }
55 }
56
57 delete[] bin;
58}
59
60int main()
61{
62 srand(time(0));
63 chain<studentRecord> students;
64 studentRecord someOne;
65 for (int i = 0; i < 100; i++)
66 {
67 char Name = i % 26 + 'A';
68 someOne.name = Name;
69 someOne.score = rand() % 101;
70 students.insert(0, someOne);
71 }
72
73 binSort(students, 100);
74 cout << " ";
75 students.output(cout);
76
77 cin.get();
78
79 return 0;
80}
最后貼上一張運行效果:

欲獲取代碼,請關注我們的微信公眾號【程序猿聲】,在后台回復:listbox 。即可下載。

推薦文章:10分鍾教你用Python做個打飛機小游戲超詳細教程