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