上篇文章介紹了:產生一定範圍內的互不相等隨機整數的一種演算法。並將產生的結果存入了一個檔案,現在我們要把這些數按從小到大排序後,重新放入一個檔案。
這個問題應該怎麼解決呢?
其實這個問題是《編程珠璣》上介紹的第一個有關於美國電話排序問題的簡化,書上介紹運用位向量來解決,並將這種方法和其他的排序演算法,如歸併排序、快速排序等,做了比較。說明了它的優越性,這裡就簡單的以上面的問題探討一下利用位向量的排序演算法。有興趣的可以詳細看看《編程珠璣》上的這部分內容。
上面的問題,已知了隨機數的範圍,也就是要排序數的範圍,並且每個都是整數且不重複。那麼就可以利用位向量排序演算法。
所謂位向量就是由一些二進位組成的向量。比如我們可以用一個10位的位向量表示一個所有元素都小於10的一個正整數集合。如集合{3,8,4,6},對應的位向量就是0011010100. 其中集合中數值代表的位置對應是1,其他的是0. 這樣一對應,如果要排序的話,只需迴圈找出位向量中所有的1,按順序輸出1所對應的位置值即可。
上面介紹了位向量排序演算法的過程,那麼針對剛開始時提出的那個問題就能迎刃而解了。步驟如下:
第一步:產生一個n位的位向量(n為要排序數的最大值),並將所有位置0
第二步:逐個讀入檔案中的資料,將資料對應的位向量中的位置的值置為1
第三步:迴圈位向量,如果該位是-1,就輸出對應的整數到輸出檔案中。這樣迴圈一遍就排序完了。
具體程式如下(怎麼產生那些要排序的隨機數,就不在程式中顯示了,我的上篇文章已詳細說過):
#include <iostream>#include <fstream>#include <ctime>#include <tchar.h>#include <vector>using namespace std;int _tmain(int argc, _TCHAR* argv[]){int count=0,number=0,sum,limit;cout<<"請輸入隨機數個數和上線"<<endl;cin>>sum>>limit;while(sum>limit){cout<<"錯誤,重新輸入"<<endl;cin>>sum>>limit;}//下面的這個函數已在上篇文章中詳細說明RandomNumbers(limit,sum);fstream openFile("data.txt");fstream outPut("output.txt",ios::out);vector<int> sort(limit,0);while(openFile>>number){sort[number]=1;}for(int i=0;i!=sort.size();i++){if(sort[i]==1){outPut<<i<<"\t";count++;if(count%10==0){outPut<<endl;}}}openFile.close();outPut.close();cout<<"已將排好的數放在輸出檔案中"<<endl;cout<<endl;system("PAUSE");return 0;}
假設隨機產生10個0到50範圍內的互不相等的隨機數,然後排序,程式執行結果如下:
排序前如:
排序後如:
上面的演算法很好的解決了不相等數的排序問題,那麼如果有重複的怎麼排序呢?
其實也很簡單,只有在第二步讀檔案中數時,把對應的位向量的位置處記錄下該數出現的次數即可。
改進後代碼如下(給出了改進的代碼部分):
while(openFile>>number){sort[number]++;}for(int i=0;i!=sort.size();i++){if(sort[i]!=0){for(int j=0;j<sort[i];j++){outPut<<i<<"\t";count++;if(count%10==0){outPut<<endl;}}}}
假設隨機產生10個0到50範圍內的隨機數(不一定互不相等),然後排序,程式執行結果如下
排序前:
排序後: