排序算法综合实验.docx
《排序算法综合实验.docx》由会员分享,可在线阅读,更多相关《排序算法综合实验.docx(17页珍藏版)》请在冰豆网上搜索。
排序算法综合实验
排序算法综合实验
1、实验目的
通过上机来体验和掌握课本的有关基本知识,加深对排序算法的认识
2、实验要求
1.实现基本排序方法:
直接插入、希尔、直接选择、冒泡、快速、堆、二路归并;
2.每种基本排序方法尽量给出多种实现(包括改进);
3.给出实验结果:
(1)随机生成若干个随机数进行排序(如n=104,2*104,105,…等),记录每个排序的时间耗费、比较次数、移动次数。
(2)分别给出正序和反序的初始序列进行排序,检验算法对初始序列的敏感程度。
(3)给出实验结果、原因分析、结论等。
(4)所有实验结果汇集成一张表。
3、几种排序算法
1、直接插入排序
1.1原理
在排序过程中,每次都将无序区中第1条记录插入到有序区中适当位置,使其仍保持有序。
初始时,取第1条记录为有序区,其他记录为无序区。
随着排序过程的进行,有序区不断扩大,无序区不断缩小。
最终无序区为空,有序区包含了全部记录,排序结束。
1.2算法
1.2.1带监视哨
voidInsertSort(listR,intn){
inti,j;
for(i=0;i<=n;i++){//依次插入R[2],R[3],...,R[n]
if(CPP,R[i].key>=R[i-1].key)continue;//R[i]大于有序区最后一个记录,不需要插入
MPP,R[0]=R[i];//R[0]是监视哨
j=i-1;
do{//查找R[i]的插入位置
MPP,R[j+1]=R[j];j--;//记录后移,继续向前搜索
}while(CPP,R[0].key=1
MPP,R[j+1]=R[0];//插入R[i]
}
}
1.2.2无监视哨
voidInsertSort2(listR,intn){//直接插入排序,无监视哨
inti,j;rectypex;//x为辅助量
for(i=2;i<=n;i++){//进行n-1次插入
for(CPP,R[i].key>=R[i-1].key)continue;
MPP,x=R[i];//待排记录暂存到x
j=i-1;
do{//顺序比较和移动
MPP,R[j+1]=R[j];j--;
}while(j>=1&&(CPP,x.keyMPP,R[j+1]=x;//插入R[i]
}
}
1.2.3改进:
在查找插入位置时采用二分查找,即二分插入排序
voidInsertSort3(listR,intn){
inti,j,low,high,mid;
for(i=2;i<=n;i++){//依次插入R[2],R[3],...,R[n]
if(CPP,R[i].key>=R[i-1].key)continue;//R[i]大于有序区最后一个记录,不需插入
MPP,R[0]=R[i];
low=1;high=i-1;
while(low<=high){//查找R[i]的插入位置
mid=(low+high)/2;
if(CPP,R[0].keyelselow=mid+1;
}
for(j=i-1;j>=high+1;j--)
MPP,R[j+1]=R[j];//记录后移
MPP,R[high+1]=R[0];
}
}
2、希尔排序
2.1原理
将数据表分成若干组,所有相隔为某个“增量”的记录为一组,在各组内进行直接插入排序;初始时增量d1较大,分组较多(每组的记录数少),以后增量逐渐减少,分组减少(每组的记录数增多),直到最后增量为1(d1>d2>...>dt=1),所有记录为同一组,再整体进行一次直接插入排序。
2.2算法
voidShellSort(listR,intn){
inth,i,j,k;
for(h=n/2;h>=1;h=h/2){
for(i=1;i<=h;i++){//i为组号
for(j=i+h;j<=n;j+=h){//每组从第2个记录开始插入
if(CPP,R[j].key>=R[j-h].key)continue;//R[j]大于有序区最后一个记录,则不需要插入
MPP,R[0]=R[j];//R[0]保存待插入记录,但不是监视哨
k=j-h;//待插记录的前一个记录
do{//查找正确的插入位置
MPP,R[k+h]=R[k];k=k-h;//后移记录,继续向前搜索
}while(k>0&&(CPP,R[0].keyMPP,R[k+h]=R[0];//插入R[j]
}
}
if(h==1)break;
}
}
3、直接选择排序
3.1原理
首先,所有记录组成初始无序区R[1]~R[n],从中选出最小的记录,与无序区第一个记录R[1]交换;新的无序区为R[2]~R[n],从中再选出最小的记录,与无序区第一个记录R[2]交换;类似,滴i趟排序时R[1]~R[i-1]是有序区,无序区为R[i]~R[n],从中选出最小的记录,将它与无序区第一个记录R[i]交换,R[1]~R[i]为新的有序区。
因为没糖排序都使有序区中增加一个记录,所以,进行n-1趟排序后,整个数据表就全部有序了。
3.2算法
voidSelectSort(listR,intn){
inti,j,k;
for(i=1;i<=n-1;i++){//n-1趟排序
k=i;
for(j=i+1;j<=n;j++)//在当前无序区中找最小的记录R[k]
if(R[j].key<=R[k].key)CPP,k=j;
if(k!
=i){MP3,R[0]=R[i];R[i]=R[k];R[k]=R[0];}//交换R[i]和R[k],R[0]作辅助
}
}
4、冒泡排序
4.1原理
设想数据表R[1]~R[n]垂直放置,将每个记录R[i]看作是重量为R[i].key的气泡;根据轻气泡不能在重气泡之下的原则,从下往上扫描数组R,凡违反本原则的轻气泡,就使其向上“漂浮”,如此反复进行,直到最后任何两个气泡都是轻者在上,重者在下为止。
4.2算法
4.2.1上升法
voidBubbleSort1(listR,intn){
inti,j,flag;rectypex;//x为辅助量(可用R[0]代替)
for(i=1;i<=n-1;i++){//做n-1趟扫描
flag=0;//置未交换标志
for(j=n;j>=i+1;j--)//从下向上扫描
if(CPP,R[j].keyflag=1;
MP3,x=R[j];R[j]=R[j-1];R[j-1]=x;//交换
}
if(!
flag)break;//本趟未交换过记录,排序结束
}
}
4.2.2下沉法
voidBubbleSort2(listR,intn){
inti,j,flag;rectypex;//x为辅助量(可用R[0]代替)
for(i=1;i<=n-1;i++){//做n-1趟扫描
flag=0;//置未交换标志
for(j=1;j<=n-i;j++)//从上向下扫描
if(CPP,R[j].key>R[j+1].key){//交换记录
flag=1;
MP3,x=R[j];R[j]=R[j+1];R[j+1]=x;//交换
}
if(!
flag)break;//本趟未交换过记录,排序结束
}
}
4.3.3改进:
双向冒泡排序,每趟排序同时使轻气泡向上“漂浮”,重气泡向下“漂浮”
voidBubbleSort3(listR,intn){//双向冒泡排序
inti,j,k,flag;rectypex;
i=1;j=n;
while(iflag=0;//置未交换标志
for(k=j;k>=i+1;k--)//从下向上扫描
if(CPP,R[k].keyflag=1;
MP3,x=R[k];R[k]=R[k-1];R[k-1]=x;//交换
}
if(!
flag)break;//本趟未交换过记录,排序结束
i++;
flag=0;
for(k=i;k<=j-1;k++)//从上向下扫描
if(CPP,R[k].key>R[k+1].key){//交换记录
flag=1;
MP3,x=R[k];R[k]=R[k+1];R[k+1]=x;
}
if(!
flag)break;
j--;
}
}
5、快速排序
5.1原理
在数据表中任取一个作为“基准”,将其余记录分为两组,第一组找那个个记录均小于或等于基准,第二组中个记录均大于或等于基准,而基准就排在这两组中间(这也是该记录的最终位置),这称为一趟快速排序(或一次划分)。
对所分的两组记录分别重复上述方法,直到每组只有1个记录为止。
5.2算法
5.2.1一趟划分算法
intPartition(listR,intp,intq){//对R[p]到R[q]划分,返回基准位置
inti,j;rectypex;//辅助量(可用R[0]代替)
i=p;j=q;MPP,x=R[p];//x存基准(无序区第一个记录)
do{
while((CPP,R[j].key>=x.key)&&iif(iwhile((CPP,R[i].key<=x.key)&&iif(i}while(iMPP,R[i]=x;//基准移到最终位置
returni;//最后i=j
}
5.2.2主算法
voidQuickSort1(listR,ints,intt){
inti;
if(s>=t)return;//只有一个记录或无记录时无需排序
i=Partition(R,s,t);//对R[s]到R[t]做划分
QuickSort1(R,s,i-1);//递归处理左区间
QuickSort1(R,i+1,t);//递归处理右区间
}
6、堆排序
6.1原理
将待排序的记录序列建成一个堆,并借助于堆的性质进行的排序方法就是堆排序。
它的原理:
将原始的n个记录序列建成一个大根堆,称为初始堆,然后将它的根和最后的元素交换,除此之外的n-1个记录序列再重复上面的操作,直到记录序列为一个递增的序列。
因此,堆排序的操作分为建立初始堆和用堆进行排序两步操作。
6.2算法
6.2.1建立初始堆算法
6.2.1.1非递归算法
voidSift1(listR,intp,intq)//堆范围为R[p]~R[q],调整R[p]为堆
{intj;
MPP,R[0]=R[p];//R[0]作辅助量
j=2*p;
while(j<=q)
{
if(jif(CPP,R[0].key>=R[j].key)break;//跟结点键值大于孩子结点,已经是堆,调整结束
MPP,R[p]=R[j];//将R[j]换到双亲位置上
p=j;//修改当前被调整结点
j=2*p;//j指向R[p]的左孩子
}
MPP,R[p]=R[0];
}
6.2.1.2递归算法
voidSift2(listR,intp,intq)//堆范围为R[p]~R[q],调整R[p]为堆
{
intj;
if(p>=q)return;//只有一个元素
j=2*p;
if(j>q)return;
if(jif(CPP,R[p].key>=R[j].key)return;//根结点关键字已最大
MPP,R[0]=R[j];//交换R[j]和R[p],R[0]作辅助量
MPP,R[j]=R[p];MPP,R[p]=R[0];Sift2(R,j,q);
}
6.2.2堆排序算法
voidHeapSort(listR,intn)//堆排序主程序
{
inti;
for(i=n/2;i>=1;i--)Sift1(R,i,n);//建初始堆
for(i=n;i>=2;i--){//进行n-1趟堆排序
MPP,R[0]=R[1];//堆顶和当前堆底交换,R[0]作辅助量
MPP,R[1]=R[i];MPP,R[i]=R[0];Sift1(R,1,i-1);
}
7、二路归并排序
7.1原理
将待排序记录R[0]到R[n-1]看成n个长度为1的有序子序列区,从第一个子序列开始,把相邻的子序列两两归并,便得到[n/2](取整数)个长度为2或1有序的子序列。
然后再把这[n/2]个有序的子序列利用上面的方法两两归并,如此反复,直到最后得到一个长度为n的有序序列。
7.2算法
7.2.1一趟归并算法
voidMerge(listR,listR1,intlow,intmid,inthigh){
//合并R的两个子表:
R[low]~R[mid]、R[mid+1]~R[high],结果在R1中
inti,j,k;
i=low;
j=mid+1;
k=low;
while(i<=mid&&j<=high)
if(CPP,R[i].key<=R[j].key)MPP,R1[k++]=R[i++];//取小者复制
elseMPP,R1[k++]=R[j++];
while(i<=mid)MPP,R1[k++]=R[i++];//复制左子表的剩余记录
while(j<=high)MPP,R1[k++]=R[j++];//复制右子表的剩余记录
}
voidMergePass(listR,listR1,intn,intlen){//对R做一趟归并,结果在R1中
inti,j;
i=1;//i指向第一对子表的起始点
while(i+2*len-1<=n){//归并长度为len的两个子表
Merge(R,R1,i,i+len-1,i+2*len-1);
i=i+2*len;//i指向下一对子表起始点
}
if(i+len-1Merge(R,R1,i,i+len-1,n);
else//子表个数为奇数,剩一段
for(j=i;j<=n;j++)//将最后一个子表复制到R1中
MPP,R1[j]=R[j];
}
7.2.2二路归并算法
voidMergeSort(listR,listR1,intn){//对R二路归并排序,结果在R中(非递归)
intlen;
len=1;
while(lenMergePass(R,R1,n,len);len=len*2;//奇趟归并,结果在R1中
MergePass(R1,R,n,len);len=len*2;//偶趟归并,结果在R中
}
}
4、头文件与主函数等
#include"stdio.h"
#include"fstream.h"
#include
#include
#include
#include
#defineCPPC++
#defineMPPM++
#defineMP2M+=2
#defineMP3M+=3
constintd=3;
constintr=10;
constintmaxsize=10000000;//数据表容量
typedefintdatatype;
typedefstruct
{
intf,e;
}queue;
typedefstruct{
datatypekey;//关键字域
datatypekey1[d];
intnext;
}rectype;//记录类型
typedefrectypelist[maxsize+2];//数据表类型,0号单元不用
__int64C,M;//比较和移动次数
voidcheck(listR,intn){//检验排序结果
inti;
for(i=2;i<=n;i++)
if(R[i].key\n";return;}
cout<<"Correct!
";
}
voiddisp(listR,intn){//显示排序后的结果
inti;
for(i=1;i<=n;i++){
cout<//if(i%20==0)cout<}
cout<}
…………
intrandom1(intnum){returnrand();}//0~RAND_MAX=32767
intrandom3(intnum){//素数模乘同余法,0~M
intA=16807;//16807,39722040,764261123,63036001648271?
intM=2147483647;//有符号4字节最大素数,2^31-1
intQ=M/A;
intR=M%A;
staticintx=1,n=0,g=0;//seed(setto1)
staticdoubler,r1=0,r2=0;
intx1;
x1=A*(x%Q)-R*(x/Q);
if(x1>=0)x=x1;
elsex=x1+M;
returnx;
}
intmain(){
rectype*R,*R1,*S;//R1用于归并排序的辅助存储,S用于保存初始排序数据
R=newlist;if(R==NULL){cout<<"数组太大!
\n";exit(-1);}
R1=newlist;if(R1==NULL){cout<<"数组太大!
\n";exit(-1);}
S=newlist;if(S==NULL){cout<<"数组太大!
\n";exit(-1);}
inti,n=maxsize;
intchoice;
clock_tt1,t2;
floats,t;
//for(i=1;i<=n;i++)//正序序列
//S[i].key=i;
//for(i=1;i<=n;i++)//反序排序
//S[i].key=n-i+1;
//srand((unsigned)time(NULL));
for(i=1;i<=n;i++)
S[i].key=random3(n);//生成0-n之间的随机数
do{
C=M=0;
for(i=1;i<=n;i++)
R[i].key=S[i].key;//取出初始数据用于排序
cout<<"选择排序方法(0:
退出):
\n\
11:
直接插入(带监视哨)12:
直接插入(无监视哨)\n\
13:
二分插入排序\n\
21:
冒泡(上升)22:
冒泡(下沉)\n\
23:
双向冒泡排序\n\
31:
快速(递归)\n\
41:
二路归并(非递归)\n\
51:
堆排序(非递归)52:
堆排序(递归)\n\
61:
选择排序\n\
71:
希尔排序\n";
cin>>choice;
switch(choice){
case11:
t1=clock();InsertSort1(R,n);t2=clock();
break;
case12:
t1=clock();InsertSort2(R,n);t2=clock();
break;
case13:
t1=clock();InsertSort3(R,n);t2=clock();
break;
case21:
t1=clock();BubbleSort1(R,n);t2=clock();
break;
case22:
t1=clock();BubbleSort2(R,n);t2=clock();
break;
case23:
t1=clock();BubbleSort3(R,n);t2=clock();
break;
case31:
t1=clock();QuickSort1(R,1,n);t2=clock();
break;
case41:
t1=clock();MergeSort(R,R1,n);t2=clock();
break;
case51:
t1=clock();HeapSort1(R,n);t2=clock();
break;
case52:
t1=clock();HeapSort2(R,n);t2=clock();
break;
case61:
t1=clock();SelectSort(R,n);t2=clock();
break;
case71:
t1=clock();ShellSort(R,n);t2=clock();
break;
default:
;
}
check(R,n);
//disp(R,n);
cout<<"C="<cout<<"时间:
"<}while(choice!
=0);
deleteR;deleteS;
//deleteR1;
}
5、实验结果与分析
1、实验结果
实验数据及结果见首页汇总表。
2、实验分析
2.1直接插入排序
该算法虽然简单,但效率不高。
由汇总表的数据可以看出,若初始数据为正序,总的关键字比较次数为最小,并且总的移