上篇文章介绍了:生成一定范围内的互不相等随机整数的一种算法。并将生成的结果存入了一个文件,现在我们要把这些数按从小到大排序后,重新放入一个文件。
这个问题应该怎么解决呢?
其实这个问题是《编程珠玑》上介绍的第一个有关于美国电话排序问题的简化,书上介绍运用位向量来解决,并将这种方法和其他的排序算法,如归并排序、快速排序等,做了比较。说明了它的优越性,这里就简单的以上面的问题探讨一下利用位向量的排序算法。有兴趣的可以详细看看《编程珠玑》上的这部分内容。
上面的问题,已知了随机数的范围,也就是要排序数的范围,并且每个都是整数且不重复。那么就可以利用位向量排序算法。
所谓位向量就是由一些二进制组成的向量。比如我们可以用一个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范围内的随机数(不一定互不相等),然后排序,程序执行结果如下
排序前:
排序后: