数据结构课程设计实验报告内部排序算法比较.docx
《数据结构课程设计实验报告内部排序算法比较.docx》由会员分享,可在线阅读,更多相关《数据结构课程设计实验报告内部排序算法比较.docx(36页珍藏版)》请在冰豆网上搜索。
数据结构课程设计实验报告内部排序算法比较
内部排序算法比较
【实验简介】
1、在教科书各种内部排序算法的时间复杂度分析结果只给出了算法执行的时间的阶,或大概的执行时间,如:
直接插入排序即时间复杂度为O(n*n)
2、通过五组随机数据、一组正序数据与一组逆序数据比较6种常用的内部算法(起泡排序、直接插入排序、简单选择排序、快速查找排序、希尔排序、堆排序)的关键字比较次数和关键字移动次数,以取得直观感受;
3、用五组不同的长度不小于100的数据进行测试,并对测试结果作出简单的分析,对得出结果拨动大小的进行解释;
【设计模块】
【对应模块算法说明】
(1)此算法程序中需要用到顺序表数据类型,数据元素的类型定义如下:
typedefstruct{
KeyTypekey;//关键字项
}RedType;
RedTyper[MAXSIZE+1];//0号单元闲置或用作哨兵单元
intlength;//顺序表长度
intinfo;//记录关键字移动次数
intcmp;//关键字的比较次数
}Sqlist;
(2)本实验用到六种排序算法,一个主函数和菜单函数,其中排序算法分别为起泡排序、直接插入排序、简单选择排序、快速查找排序、希尔排序、堆排序;相应时间复杂度分析如下:
起泡排序:
若待排序列为“正序”,则需进行一趟排序在排序过程中关键字需进行n-1次比较,无须移动纪录;若是“逆序”,则进行n-1趟排序,需n(n-1)/2次比较,并坐等数量级的移动,因此总的事件复杂度为O(n2);
直接插入排序待排序纪录是随机的,关键字间的移动次数和比较次数的平均值约为n*n/4,即时间复杂度为O(n2);
简单的选择排序虽然在排序过程中所需要的移动次数较少,最小时为0,最大时为3(n-1);但是关键字的比较次数总是相同的,均为n(n-1)/2,因此,时间复杂度亦为O(n2);
快速排序其平均时间是kn*㏑n,其中n为待排序列中纪录的个数,k为某个常数,其时间复杂度为O(n*㏑n);
希尔排序当增序序列为dlta[k]=2t-k+1-1时,时间复杂度为O(n3/2),其中t为排序趟数,1≤k≤t≤㏒2(n+1);
堆排序此排序对于含n个元素的序列排序时,总共进行的关键字比较次数不超过4n,且在最坏的情况下,其时间复杂度为O(n*㏑n);
算法分析如下:
①冒泡排序该算法的的思路是首先将第1个记录的关键字负值给L.r[0],然后用L.r[0]与第(i+1)个记录的关键字比较,若为逆序,则交换第i与第i+1两记录的位置,然后让i加1,重复以上操作,直至i=n-1为止;依次进行第二趟、第三趟……作同样的操作,直至所有的记录按正序排列(一般需要n-1趟比较,第i趟从L.r[1]到L.r[n-i+1]依次比较,1≦i≦n-i,比较结果是让其中最大的记录放在L.r[n-i+1]的位置)
voidBubbleSort(Sqlist*L)//冒泡排序
{
for(i=0;ilength;i++)
L->r[0]=L->r[1];
for(j=1;jif(L->r[0].key>=L->r[j+1].key)L->r[j]L->r[j+1];//交换两记录的位置elseL->r[0]=L->r[j+1];//保证L->r[0]始终为较大的记录L->r[j+1]=L->r[0];//把最大的记录放到祠堂排序的最高位置}printf(L->r[MAXSIZE]);//输出排序后数组和相关数据}②直接插入排序本算法的思路是,把L->r[0]设置为哨兵,排序时把第i个记录复制给哨兵,并于其前的i-1个记录进行比较,找到第一个比起大的记录,利用循环让记录后移,把其放到第一个比起大的记录前面。voidinsertSort(Sqlist*L)//直接插入排序{for(i=2;i<=L->length;i++){if((L->r[i].keyr[i-1].key)){L->r[0]=L->r[i];//复制哨兵L->r[i]=L->r[i-1];for(j=i-2;(L->r[0].keyr[j].key);j--)L->r[j+1]=L->r[j];//纪录后移L->r[j+1]=L->r[0];//插入到正确位置}}printf(L->r[MAXSIZE]);//输出排序后数组和相关数据}③简单选择排序基本思想是在第i趟选择排序时:通过n-i次关键字之间的比较,从n-i+1个记录中选出关键字最小的记录,并和第i(1≦i≦n)个记录交换之voidSelectSort(Sqlist*L)//简单选择排序{for(i=1;i<=L->length;++i)//选择第i小的记录,并交换到位{L->r[0]=L->r[i];j=i;for(k=i+1;k<=L->length;k++)//从L.r[i..L.length]中选key最小的if(L->r[0].key>L->r[k].key){L->r[0]=L->r[k];j=k;}if(i!=j)L.r[i]←→L.r[j];//与第i个记录交换}printf(L->r[MAXSIZE]);//输出排序后数组和相关数据}④快速查找排序其基本思想为,通过一趟排序将带排序记录分割成两部分,其中一部分记录的关键字均小于另一部分的关键字,再利用递归分别对分割所得到的子序列进行快速排序。其中一趟快速排序的算法为:intserchSort(Sqlist*L,intlow,inthigh)//快速排序单次排序{pivotkey=L->r[low].key;//枢轴纪录关键字while(low{while(lowr[high].key>=pivotkey)high--;L->r[low]L->r[high];//将比枢轴纪录小的纪录移到底端while(lowr[low].key<=pivotkey)low++;L->r[high]L->r[low];//将比枢轴纪录大的纪录移到高端}returnhigh;//返回枢轴所在位置}递归算法为:voidQSort(Sqlist*L,intlow,inthigh)//快速排序{if(low{//长度大于1pivotloc=serchSort(L,low,high);//调用serchSort()将L.r[low..high]一分为二QSort(L,low,pivotloc-1);//对低子表递归排序,pivotloc是枢轴位置QSort(L,pivotloc+1,high);//对高子表递归排序}}⑤希尔排序基本思路是先将整个待排序的记录分割成若干个子序列进行直接排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次全体直接插入排序voidShellSort(Sqlist*L,intdlta[],intt)//希尔排序{for(k=1;k<=t;k++){dlta[k]=(int)pow(2,t-k+1)-1;//doublepow(doublebase,doubleexp);函数返回以参数base为底的exp次幂for(i=dlta[k]+1;i<=L->length;i++)if(L->r[i].keyr[i-dlta[k]].key){L->r[0]=L->r[i];for(j=i-dlta[k];j>0&&(L->r[0].keyr[j].key);j-=dlta[k])L->r[j+dlta[k]]=L->r[j];L->r[j+dlta[k]]=L->r[0];}}printf(L->r[MAXSIZE]);//输出排序后数组和相关数据}⑥堆排序基本思想是使记录序列按关键字非递减有序排列,则再对排序的算法中先建立一个“大顶堆”,即先选得一个关键字为最大的记录与序列中最后一个记录交换,然后再对序列中前n-1记录进行筛选,重新将它调整为“大顶堆“,如此反复直至排序结束。voidHeapAdjust(Sqlist*L,ints,intm){//已知L.r[s..m]中记录的关键字除L.r[s].key之外均满足堆的定义,本函数调整L.r[s]的关键字,使L.r[s..m]成为一个大顶堆(对其中记录的关键字而言)rc=L->r[s];for(j=2*s;j<=m;j*=2){//沿key较大的孩子结点向下筛选if(jr[j].keyr[j+1].key)++j;//j为key较大记录的下标if(rc.key>=L->r[j].key)break;//rc应插入在位置s上L->r[s]=L->r[j];s=j;}L->r[s]=rc;//插入}voidHeapSort(Sqlist*L)//堆排序{for(i=L->length/2;i>0;i--)//把H->r[…]建成大顶堆HeapAdjust(L,i,L->length);for(i=L->length;i>1;i--)L->r[i]L->r[1];//将堆顶记录和当前未经排序子序列L.r[1…i]中//最后一个记录相互交换HeapAdjust(L,1,i-1);//将H.r[1..i-1]重新调整为大顶堆}printf(L->r[MAXSIZE]);//输出排序后数组和相关数据}⑦菜单程序利用dowhile循环实现对菜单的正确选择,并返回选择的操作代码intmenu_select()//菜单目录{intnu;chars;printf("************************************************\n");printf("*****菜单*****\n");printf("1.BubbleSort\n");//冒泡排序printf("2.insertSort\n");//直接插入排序printf("3.SelectSort\n");//简单选择排序printf("4.QSort\n");//查找排序printf("5.ShellSort\n");//希尔排序printf("6.HeapSort\n");//堆排序printf("7.退出程序\n");printf("************************************************\n");printf("请输入数字:1-7选择相应的排序方式或操作:\n");do{s=getchar();nu=(int)s-48;}while(nu<0||nu>55);return(nu);}⑧主函数是程序的入口,包括两个for循环,一个用来实现随机数产生与输出;另一个用来实现菜单的选择的连续;第二个for循环中包含一个switch循环,作用是根据菜单程序的返回值选择相应的操作,随后作相应的修改使待排序数据分别为正序和逆序,以实现本设计的目的。voidmain(){printf("输出待排序的数据:");for(;;)switch(menu_select())//选择相应的排序操作{case1:BubbleSort(&A);break;case2:insertSort(&B);break;case3:SelectSort(&C);break;case4:QSort(&L,1,L.length);break;case5:ShellSort(&D,dlta,t);break;case6:HeapSort(&E);break;case7:exit(0);}}运行结果:第一组随机数据及其运行结果如下:第二组随机数据及其运行结果如下:第三组随机数据及其运行结果如下:第四组随机数据及其运行结果如下:第五组随机数据及其运行结果如下:第六组正序数据及其运行结果如下:第七组逆序数据及其运行结果如下:【小结与讨论】表中数据为关键字移动次数:BubbleSortinsertSortSelectSortQSortShellSortHeapSort第一组697426906053937231082第二组706624735863827421081第三组707227335924087241075第四组706825875833687881087第五组696327876204027541085第六组99000029701136第七组4950514826502975401012下表中数据为关键字比较次数:BubbleSortinsertSortSelectSortQSortShellSortHeapSort第一组495025044950638785749第二组495022934950635790752第三组495025504950600796752第四组495024044950625824748第五组495026074950621804745第六组49509949504950480811第七组4950495049504950672662有数据分析可知:无论待排序列排序如何,BubbleSort与SelectSort两种算法的关键字比较次数均为4950,即n(n-1)/2次;待排序列为随机序列时直接插入排序的关键字比较次数和关键字移动平均值约为n2/4,正序时比较次数为(n-1),移动次数为0,逆序时为n(n-1)/2次;整体看来六种排序中QSort、ShellSort、HeapSort三种排序比较和移动次数相对较少;但BubbleSort、insertSort两种排序属于稳定性排序(每交换一次纪录,关键字就有三次移动纪录)。在测试过程中,当连续选择一个操作时,发现有的排序方式的关键字移动次数会随之增加,例如BubbleSort,QSort,HeapSort;仔细分析后得知,无论待排序列是否排好,它每次运行都会有关键字的移动;每次运行程序,六种排序方式的关键字比较次数也会随着操作次数的增加而增加,原因与上一个问题先同。 【程序清单】#include#include#include#include#include#defineMAXSIZE100typedefintKeyType;//定义关键字的整数类型typedefstruct{KeyTypekey;//关键字项}RedType;typedefstruct{RedTyper[MAXSIZE+1];//0号单元闲置或用作哨兵单元intlength;//顺序表长度intinfo;//记录关键字移动次数intcmp;//关键字的比较次数}Sqlist;/**********************************************************************/voidBubbleSort(Sqlist*L)//冒泡排序{inti,j;intN=L->length;for(i=0;ilength;i++){L->r[0]=L->r[1];for(j=1;j{if(L->r[0].key>=L->r[j+1].key){L->r[j]=L->r[j+1];L->info++;}else{L->r[j]=L->r[0];L->r[0]=L->r[j+1];L->info+=2;}L->cmp++;L->r[j+1]=L->r[0];}}printf("以下数据的排列顺序为BubbleSort排序后的排列顺序:\n");for(i=1;i<=MAXSIZE;i++)printf("%d\t",L->r[i]);//输出排序后数组和相关数据printf("关键字移动次数为:%d\n",L->info);printf("关键字比较次数为:%d\n",L->cmp);}/**********************************************************************/voidinsertSort(Sqlist*L)//直接插入排序{inti,j;for(i=2;i<=L->length;i++){L->cmp++;if((L->r[i].keyr[i-1].key)){L->r[0]=L->r[i];//复制哨兵L->r[i]=L->r[i-1];L->info+=2;for(j=i-2;(L->r[0].keyr[j].key);j--){L->r[j+1]=L->r[j];//纪录后移L->info++;L->cmp++;}L->r[j+1]=L->r[0];//插入到正确位置L->info++;}}printf("以下数据的排列顺序为insertSort排序后的排列顺序:\n");for(i=1;i<=L->length;i++)printf("%d\t",L->r[i]);//输出排序后数组和相关数据printf("关键字移动次数为:%d\n",L->info);printf("关键字比较次数为:%d\n",L->cmp);}/**********************************************************************/voidSelectSort(Sqlist*L)//简单选择排序{inti,j,k;for(i=1;i<=L->length;++i)//选择第i小的记录,并交换到位{L->r[0]=L->r[i];j=i;for(k=i+1;k<=L->length;k++)//在L.r[i..L.length]中选key最小者{L->cmp++;if(L->r[0].key>L->r[k].key){L->r[0]=L->r[k];j=k;L->info++;}}if(i!=j){//L.r[i]←→L.r[j];与第i个记录交换RedTypetemp;temp=L->r[i];L->r[i]=L->r[j];L->r[j]=temp;L->info+=3;}}printf("以下数据的排列顺序为SelectSort排序后的排列顺序:\n");for(i=1;i<=L->length;i++)printf("%d\t",L->r[i]);//输出排序后数组和相关数据printf("关键字移动次数为:%d\n",L->info);printf("关键字比较次数为:%d\n",L->cmp);}/**********************************************************************/intserchSort(Sqlist*L,intlow,inthigh)//查找排序单次排序{intpivotkey;L->r[0]=L->r[low];pivotkey=L->r[low].key;//枢轴纪录关键字while(low{while(lowr[high].key>=pivotkey){high--;L->cmp++;}L->r[low]=L->r[high];//将比枢轴纪录小的纪录移到底端L->info++;while(lowr[low].key<=pivotkey){low++;L->cmp++;}L->r[high]=L->r[low];//将比枢轴纪录大的纪录移到高端L->info++;}L->r[low]=L->r[0];L->info++;returnhigh;}voidQSort(Sqlist*L,intlow,inthigh)//快速排序{intpivotloc;if(low{//长度大于1pivotloc=serchSort(L,low,high);//将L.r[low..high]一分为二QSort(L
if(L->r[0].key>=L->r[j+1].key)
L->r[j]L->r[j+1];//交换两记录的位置
else
L->r[0]=L->r[j+1];//保证L->r[0]始终为较大的记录
L->r[j+1]=L->r[0];//把最大的记录放到祠堂排序的最高位置
}
printf(L->r[MAXSIZE]);//输出排序后数组和相关数据
②直接插入排序本算法的思路是,把L->r[0]设置为哨兵,排序时把第i个记录复制给哨兵,并于其前的i-1个记录进行比较,找到第一个比起大的记录,利用循环让记录后移,把其放到第一个比起大的记录前面。
voidinsertSort(Sqlist*L)//直接插入排序
for(i=2;i<=L->length;i++)
if((L->r[i].keyr[i-1].key))
L->r[0]=L->r[i];//复制哨兵
L->r[i]=L->r[i-1];
for(j=i-2;(L->r[0].keyr[j].key);j--)
L->r[j+1]=L->r[j];//纪录后移
L->r[j+1]=L->r[0];//插入到正确位置
③简单选择排序基本思想是在第i趟选择排序时:
通过n-i次关键字之间的比较,从n-i+1个记录中选出关键字最小的记录,并和第i(1≦i≦n)个记录交换之
voidSelectSort(Sqlist*L)//简单选择排序
for(i=1;i<=L->length;++i)//选择第i小的记录,并交换到位
L->r[0]=L->r[i];j=i;
for(k=i+1;k<=L->length;k++)//从L.r[i..L.length]中选key最小的
if(L->r[0].key>L->r[k].key)
L->r[0]=L->r[k];
j=k;
if(i!
=j)
L.r[i]←→L.r[j];//与第i个记录交换
④快速查找排序其基本思想为,通过一趟排序将带排序记录分割成两部分,其中一部分记录的关键字均小于另一部分的关键字,再利用递归分别对分割所得到的子序列进行快速排序。
其中一趟快速排序的算法为:
intserchSort(Sqlist*L,intlow,inthigh)//快速排序单次排序
pivotkey=L->r[low].key;//枢轴纪录关键字
while(low{while(lowr[high].key>=pivotkey)high--;L->r[low]L->r[high];//将比枢轴纪录小的纪录移到底端while(lowr[low].key<=pivotkey)low++;L->r[high]L->r[low];//将比枢轴纪录大的纪录移到高端}returnhigh;//返回枢轴所在位置}递归算法为:voidQSort(Sqlist*L,intlow,inthigh)//快速排序{if(low{//长度大于1pivotloc=serchSort(L,low,high);//调用serchSort()将L.r[low..high]一分为二QSort(L,low,pivotloc-1);//对低子表递归排序,pivotloc是枢轴位置QSort(L,pivotloc+1,high);//对高子表递归排序}}⑤希尔排序基本思路是先将整个待排序的记录分割成若干个子序列进行直接排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次全体直接插入排序voidShellSort(Sqlist*L,intdlta[],intt)//希尔排序{for(k=1;k<=t;k++){dlta[k]=(int)pow(2,t-k+1)-1;//doublepow(doublebase,doubleexp);函数返回以参数base为底的exp次幂for(i=dlta[k]+1;i<=L->length;i++)if(L->r[i].keyr[i-dlta[k]].key){L->r[0]=L->r[i];for(j=i-dlta[k];j>0&&(L->r[0].keyr[j].key);j-=dlta[k])L->r[j+dlta[k]]=L->r[j];L->r[j+dlta[k]]=L->r[0];}}printf(L->r[MAXSIZE]);//输出排序后数组和相关数据}⑥堆排序基本思想是使记录序列按关键字非递减有序排列,则再对排序的算法中先建立一个“大顶堆”,即先选得一个关键字为最大的记录与序列中最后一个记录交换,然后再对序列中前n-1记录进行筛选,重新将它调整为“大顶堆“,如此反复直至排序结束。voidHeapAdjust(Sqlist*L,ints,intm){//已知L.r[s..m]中记录的关键字除L.r[s].key之外均满足堆的定义,本函数调整L.r[s]的关键字,使L.r[s..m]成为一个大顶堆(对其中记录的关键字而言)rc=L->r[s];for(j=2*s;j<=m;j*=2){//沿key较大的孩子结点向下筛选if(jr[j].keyr[j+1].key)++j;//j为key较大记录的下标if(rc.key>=L->r[j].key)break;//rc应插入在位置s上L->r[s]=L->r[j];s=j;}L->r[s]=rc;//插入}voidHeapSort(Sqlist*L)//堆排序{for(i=L->length/2;i>0;i--)//把H->r[…]建成大顶堆HeapAdjust(L,i,L->length);for(i=L->length;i>1;i--)L->r[i]L->r[1];//将堆顶记录和当前未经排序子序列L.r[1…i]中//最后一个记录相互交换HeapAdjust(L,1,i-1);//将H.r[1..i-1]重新调整为大顶堆}printf(L->r[MAXSIZE]);//输出排序后数组和相关数据}⑦菜单程序利用dowhile循环实现对菜单的正确选择,并返回选择的操作代码intmenu_select()//菜单目录{intnu;chars;printf("************************************************\n");printf("*****菜单*****\n");printf("1.BubbleSort\n");//冒泡排序printf("2.insertSort\n");//直接插入排序printf("3.SelectSort\n");//简单选择排序printf("4.QSort\n");//查找排序printf("5.ShellSort\n");//希尔排序printf("6.HeapSort\n");//堆排序printf("7.退出程序\n");printf("************************************************\n");printf("请输入数字:1-7选择相应的排序方式或操作:\n");do{s=getchar();nu=(int)s-48;}while(nu<0||nu>55);return(nu);}⑧主函数是程序的入口,包括两个for循环,一个用来实现随机数产生与输出;另一个用来实现菜单的选择的连续;第二个for循环中包含一个switch循环,作用是根据菜单程序的返回值选择相应的操作,随后作相应的修改使待排序数据分别为正序和逆序,以实现本设计的目的。voidmain(){printf("输出待排序的数据:");for(;;)switch(menu_select())//选择相应的排序操作{case1:BubbleSort(&A);break;case2:insertSort(&B);break;case3:SelectSort(&C);break;case4:QSort(&L,1,L.length);break;case5:ShellSort(&D,dlta,t);break;case6:HeapSort(&E);break;case7:exit(0);}}运行结果:第一组随机数据及其运行结果如下:第二组随机数据及其运行结果如下:第三组随机数据及其运行结果如下:第四组随机数据及其运行结果如下:第五组随机数据及其运行结果如下:第六组正序数据及其运行结果如下:第七组逆序数据及其运行结果如下:【小结与讨论】表中数据为关键字移动次数:BubbleSortinsertSortSelectSortQSortShellSortHeapSort第一组697426906053937231082第二组706624735863827421081第三组707227335924087241075第四组706825875833687881087第五组696327876204027541085第六组99000029701136第七组4950514826502975401012下表中数据为关键字比较次数:BubbleSortinsertSortSelectSortQSortShellSortHeapSort第一组495025044950638785749第二组495022934950635790752第三组495025504950600796752第四组495024044950625824748第五组495026074950621804745第六组49509949504950480811第七组4950495049504950672662有数据分析可知:无论待排序列排序如何,BubbleSort与SelectSort两种算法的关键字比较次数均为4950,即n(n-1)/2次;待排序列为随机序列时直接插入排序的关键字比较次数和关键字移动平均值约为n2/4,正序时比较次数为(n-1),移动次数为0,逆序时为n(n-1)/2次;整体看来六种排序中QSort、ShellSort、HeapSort三种排序比较和移动次数相对较少;但BubbleSort、insertSort两种排序属于稳定性排序(每交换一次纪录,关键字就有三次移动纪录)。在测试过程中,当连续选择一个操作时,发现有的排序方式的关键字移动次数会随之增加,例如BubbleSort,QSort,HeapSort;仔细分析后得知,无论待排序列是否排好,它每次运行都会有关键字的移动;每次运行程序,六种排序方式的关键字比较次数也会随着操作次数的增加而增加,原因与上一个问题先同。 【程序清单】#include#include#include#include#include#defineMAXSIZE100typedefintKeyType;//定义关键字的整数类型typedefstruct{KeyTypekey;//关键字项}RedType;typedefstruct{RedTyper[MAXSIZE+1];//0号单元闲置或用作哨兵单元intlength;//顺序表长度intinfo;//记录关键字移动次数intcmp;//关键字的比较次数}Sqlist;/**********************************************************************/voidBubbleSort(Sqlist*L)//冒泡排序{inti,j;intN=L->length;for(i=0;ilength;i++){L->r[0]=L->r[1];for(j=1;j{if(L->r[0].key>=L->r[j+1].key){L->r[j]=L->r[j+1];L->info++;}else{L->r[j]=L->r[0];L->r[0]=L->r[j+1];L->info+=2;}L->cmp++;L->r[j+1]=L->r[0];}}printf("以下数据的排列顺序为BubbleSort排序后的排列顺序:\n");for(i=1;i<=MAXSIZE;i++)printf("%d\t",L->r[i]);//输出排序后数组和相关数据printf("关键字移动次数为:%d\n",L->info);printf("关键字比较次数为:%d\n",L->cmp);}/**********************************************************************/voidinsertSort(Sqlist*L)//直接插入排序{inti,j;for(i=2;i<=L->length;i++){L->cmp++;if((L->r[i].keyr[i-1].key)){L->r[0]=L->r[i];//复制哨兵L->r[i]=L->r[i-1];L->info+=2;for(j=i-2;(L->r[0].keyr[j].key);j--){L->r[j+1]=L->r[j];//纪录后移L->info++;L->cmp++;}L->r[j+1]=L->r[0];//插入到正确位置L->info++;}}printf("以下数据的排列顺序为insertSort排序后的排列顺序:\n");for(i=1;i<=L->length;i++)printf("%d\t",L->r[i]);//输出排序后数组和相关数据printf("关键字移动次数为:%d\n",L->info);printf("关键字比较次数为:%d\n",L->cmp);}/**********************************************************************/voidSelectSort(Sqlist*L)//简单选择排序{inti,j,k;for(i=1;i<=L->length;++i)//选择第i小的记录,并交换到位{L->r[0]=L->r[i];j=i;for(k=i+1;k<=L->length;k++)//在L.r[i..L.length]中选key最小者{L->cmp++;if(L->r[0].key>L->r[k].key){L->r[0]=L->r[k];j=k;L->info++;}}if(i!=j){//L.r[i]←→L.r[j];与第i个记录交换RedTypetemp;temp=L->r[i];L->r[i]=L->r[j];L->r[j]=temp;L->info+=3;}}printf("以下数据的排列顺序为SelectSort排序后的排列顺序:\n");for(i=1;i<=L->length;i++)printf("%d\t",L->r[i]);//输出排序后数组和相关数据printf("关键字移动次数为:%d\n",L->info);printf("关键字比较次数为:%d\n",L->cmp);}/**********************************************************************/intserchSort(Sqlist*L,intlow,inthigh)//查找排序单次排序{intpivotkey;L->r[0]=L->r[low];pivotkey=L->r[low].key;//枢轴纪录关键字while(low{while(lowr[high].key>=pivotkey){high--;L->cmp++;}L->r[low]=L->r[high];//将比枢轴纪录小的纪录移到底端L->info++;while(lowr[low].key<=pivotkey){low++;L->cmp++;}L->r[high]=L->r[low];//将比枢轴纪录大的纪录移到高端L->info++;}L->r[low]=L->r[0];L->info++;returnhigh;}voidQSort(Sqlist*L,intlow,inthigh)//快速排序{intpivotloc;if(low{//长度大于1pivotloc=serchSort(L,low,high);//将L.r[low..high]一分为二QSort(L
while(lowr[high].key>=pivotkey)high--;
L->r[low]L->r[high];//将比枢轴纪录小的纪录移到底端
while(lowr[low].key<=pivotkey)low++;
L->r[high]L->r[low];//将比枢轴纪录大的纪录移到高端
returnhigh;//返回枢轴所在位置
递归算法为:
voidQSort(Sqlist*L,intlow,inthigh)//快速排序
if(low{//长度大于1pivotloc=serchSort(L,low,high);//调用serchSort()将L.r[low..high]一分为二QSort(L,low,pivotloc-1);//对低子表递归排序,pivotloc是枢轴位置QSort(L,pivotloc+1,high);//对高子表递归排序}}⑤希尔排序基本思路是先将整个待排序的记录分割成若干个子序列进行直接排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次全体直接插入排序voidShellSort(Sqlist*L,intdlta[],intt)//希尔排序{for(k=1;k<=t;k++){dlta[k]=(int)pow(2,t-k+1)-1;//doublepow(doublebase,doubleexp);函数返回以参数base为底的exp次幂for(i=dlta[k]+1;i<=L->length;i++)if(L->r[i].keyr[i-dlta[k]].key){L->r[0]=L->r[i];for(j=i-dlta[k];j>0&&(L->r[0].keyr[j].key);j-=dlta[k])L->r[j+dlta[k]]=L->r[j];L->r[j+dlta[k]]=L->r[0];}}printf(L->r[MAXSIZE]);//输出排序后数组和相关数据}⑥堆排序基本思想是使记录序列按关键字非递减有序排列,则再对排序的算法中先建立一个“大顶堆”,即先选得一个关键字为最大的记录与序列中最后一个记录交换,然后再对序列中前n-1记录进行筛选,重新将它调整为“大顶堆“,如此反复直至排序结束。voidHeapAdjust(Sqlist*L,ints,intm){//已知L.r[s..m]中记录的关键字除L.r[s].key之外均满足堆的定义,本函数调整L.r[s]的关键字,使L.r[s..m]成为一个大顶堆(对其中记录的关键字而言)rc=L->r[s];for(j=2*s;j<=m;j*=2){//沿key较大的孩子结点向下筛选if(jr[j].keyr[j+1].key)++j;//j为key较大记录的下标if(rc.key>=L->r[j].key)break;//rc应插入在位置s上L->r[s]=L->r[j];s=j;}L->r[s]=rc;//插入}voidHeapSort(Sqlist*L)//堆排序{for(i=L->length/2;i>0;i--)//把H->r[…]建成大顶堆HeapAdjust(L,i,L->length);for(i=L->length;i>1;i--)L->r[i]L->r[1];//将堆顶记录和当前未经排序子序列L.r[1…i]中//最后一个记录相互交换HeapAdjust(L,1,i-1);//将H.r[1..i-1]重新调整为大顶堆}printf(L->r[MAXSIZE]);//输出排序后数组和相关数据}⑦菜单程序利用dowhile循环实现对菜单的正确选择,并返回选择的操作代码intmenu_select()//菜单目录{intnu;chars;printf("************************************************\n");printf("*****菜单*****\n");printf("1.BubbleSort\n");//冒泡排序printf("2.insertSort\n");//直接插入排序printf("3.SelectSort\n");//简单选择排序printf("4.QSort\n");//查找排序printf("5.ShellSort\n");//希尔排序printf("6.HeapSort\n");//堆排序printf("7.退出程序\n");printf("************************************************\n");printf("请输入数字:1-7选择相应的排序方式或操作:\n");do{s=getchar();nu=(int)s-48;}while(nu<0||nu>55);return(nu);}⑧主函数是程序的入口,包括两个for循环,一个用来实现随机数产生与输出;另一个用来实现菜单的选择的连续;第二个for循环中包含一个switch循环,作用是根据菜单程序的返回值选择相应的操作,随后作相应的修改使待排序数据分别为正序和逆序,以实现本设计的目的。voidmain(){printf("输出待排序的数据:");for(;;)switch(menu_select())//选择相应的排序操作{case1:BubbleSort(&A);break;case2:insertSort(&B);break;case3:SelectSort(&C);break;case4:QSort(&L,1,L.length);break;case5:ShellSort(&D,dlta,t);break;case6:HeapSort(&E);break;case7:exit(0);}}运行结果:第一组随机数据及其运行结果如下:第二组随机数据及其运行结果如下:第三组随机数据及其运行结果如下:第四组随机数据及其运行结果如下:第五组随机数据及其运行结果如下:第六组正序数据及其运行结果如下:第七组逆序数据及其运行结果如下:【小结与讨论】表中数据为关键字移动次数:BubbleSortinsertSortSelectSortQSortShellSortHeapSort第一组697426906053937231082第二组706624735863827421081第三组707227335924087241075第四组706825875833687881087第五组696327876204027541085第六组99000029701136第七组4950514826502975401012下表中数据为关键字比较次数:BubbleSortinsertSortSelectSortQSortShellSortHeapSort第一组495025044950638785749第二组495022934950635790752第三组495025504950600796752第四组495024044950625824748第五组495026074950621804745第六组49509949504950480811第七组4950495049504950672662有数据分析可知:无论待排序列排序如何,BubbleSort与SelectSort两种算法的关键字比较次数均为4950,即n(n-1)/2次;待排序列为随机序列时直接插入排序的关键字比较次数和关键字移动平均值约为n2/4,正序时比较次数为(n-1),移动次数为0,逆序时为n(n-1)/2次;整体看来六种排序中QSort、ShellSort、HeapSort三种排序比较和移动次数相对较少;但BubbleSort、insertSort两种排序属于稳定性排序(每交换一次纪录,关键字就有三次移动纪录)。在测试过程中,当连续选择一个操作时,发现有的排序方式的关键字移动次数会随之增加,例如BubbleSort,QSort,HeapSort;仔细分析后得知,无论待排序列是否排好,它每次运行都会有关键字的移动;每次运行程序,六种排序方式的关键字比较次数也会随着操作次数的增加而增加,原因与上一个问题先同。 【程序清单】#include#include#include#include#include#defineMAXSIZE100typedefintKeyType;//定义关键字的整数类型typedefstruct{KeyTypekey;//关键字项}RedType;typedefstruct{RedTyper[MAXSIZE+1];//0号单元闲置或用作哨兵单元intlength;//顺序表长度intinfo;//记录关键字移动次数intcmp;//关键字的比较次数}Sqlist;/**********************************************************************/voidBubbleSort(Sqlist*L)//冒泡排序{inti,j;intN=L->length;for(i=0;ilength;i++){L->r[0]=L->r[1];for(j=1;j{if(L->r[0].key>=L->r[j+1].key){L->r[j]=L->r[j+1];L->info++;}else{L->r[j]=L->r[0];L->r[0]=L->r[j+1];L->info+=2;}L->cmp++;L->r[j+1]=L->r[0];}}printf("以下数据的排列顺序为BubbleSort排序后的排列顺序:\n");for(i=1;i<=MAXSIZE;i++)printf("%d\t",L->r[i]);//输出排序后数组和相关数据printf("关键字移动次数为:%d\n",L->info);printf("关键字比较次数为:%d\n",L->cmp);}/**********************************************************************/voidinsertSort(Sqlist*L)//直接插入排序{inti,j;for(i=2;i<=L->length;i++){L->cmp++;if((L->r[i].keyr[i-1].key)){L->r[0]=L->r[i];//复制哨兵L->r[i]=L->r[i-1];L->info+=2;for(j=i-2;(L->r[0].keyr[j].key);j--){L->r[j+1]=L->r[j];//纪录后移L->info++;L->cmp++;}L->r[j+1]=L->r[0];//插入到正确位置L->info++;}}printf("以下数据的排列顺序为insertSort排序后的排列顺序:\n");for(i=1;i<=L->length;i++)printf("%d\t",L->r[i]);//输出排序后数组和相关数据printf("关键字移动次数为:%d\n",L->info);printf("关键字比较次数为:%d\n",L->cmp);}/**********************************************************************/voidSelectSort(Sqlist*L)//简单选择排序{inti,j,k;for(i=1;i<=L->length;++i)//选择第i小的记录,并交换到位{L->r[0]=L->r[i];j=i;for(k=i+1;k<=L->length;k++)//在L.r[i..L.length]中选key最小者{L->cmp++;if(L->r[0].key>L->r[k].key){L->r[0]=L->r[k];j=k;L->info++;}}if(i!=j){//L.r[i]←→L.r[j];与第i个记录交换RedTypetemp;temp=L->r[i];L->r[i]=L->r[j];L->r[j]=temp;L->info+=3;}}printf("以下数据的排列顺序为SelectSort排序后的排列顺序:\n");for(i=1;i<=L->length;i++)printf("%d\t",L->r[i]);//输出排序后数组和相关数据printf("关键字移动次数为:%d\n",L->info);printf("关键字比较次数为:%d\n",L->cmp);}/**********************************************************************/intserchSort(Sqlist*L,intlow,inthigh)//查找排序单次排序{intpivotkey;L->r[0]=L->r[low];pivotkey=L->r[low].key;//枢轴纪录关键字while(low{while(lowr[high].key>=pivotkey){high--;L->cmp++;}L->r[low]=L->r[high];//将比枢轴纪录小的纪录移到底端L->info++;while(lowr[low].key<=pivotkey){low++;L->cmp++;}L->r[high]=L->r[low];//将比枢轴纪录大的纪录移到高端L->info++;}L->r[low]=L->r[0];L->info++;returnhigh;}voidQSort(Sqlist*L,intlow,inthigh)//快速排序{intpivotloc;if(low{//长度大于1pivotloc=serchSort(L,low,high);//将L.r[low..high]一分为二QSort(L
{//长度大于1
pivotloc=serchSort(L,low,high);//调用serchSort()将L.r[low..high]一分为二
QSort(L,low,pivotloc-1);//对低子表递归排序,pivotloc是枢轴位置
QSort(L,pivotloc+1,high);//对高子表递归排序
⑤希尔排序基本思路是先将整个待排序的记录分割成若干个子序列进行直接排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次全体直接插入排序
voidShellSort(Sqlist*L,intdlta[],intt)//希尔排序
for(k=1;k<=t;k++)
dlta[k]=(int)pow(2,t-k+1)-1;//doublepow(doublebase,doubleexp);函数返回以参数base为底的exp次幂
for(i=dlta[k]+1;i<=L->length;i++)
if(L->r[i].keyr[i-dlta[k]].key)
L->r[0]=L->r[i];
for(j=i-dlta[k];j>0&&(L->r[0].keyr[j].key);j-=dlta[k])
L->r[j+dlta[k]]=L->r[j];
L->r[j+dlta[k]]=L->r[0];
⑥堆排序基本思想是使记录序列按关键字非递减有序排列,则再对排序的算法中先建立一个“大顶堆”,即先选得一个关键字为最大的记录与序列中最后一个记录交换,然后再对序列中前n-1记录进行筛选,重新将它调整为“大顶堆“,如此反复直至排序结束。
voidHeapAdjust(Sqlist*L,ints,intm){//已知L.r[s..m]中记录的关键字除L.r[s].key之外均满足堆的定义,本函数调整L.r[s]的关键字,使L.r[s..m]成为一个大顶堆(对其中记录的关键字而言)
rc=L->r[s];
for(j=2*s;j<=m;j*=2){//沿key较大的孩子结点向下筛选
if(jr[j].keyr[j+1].key)++j;//j为key较大记录的下标
if(rc.key>=L->r[j].key)break;//rc应插入在位置s上
L->r[s]=L->r[j];
s=j;
L->r[s]=rc;//插入
voidHeapSort(Sqlist*L)//堆排序
for(i=L->length/2;i>0;i--)//把H->r[…]建成大顶堆
HeapAdjust(L,i,L->length);
for(i=L->length;i>1;i--)
L->r[i]L->r[1];//将堆顶记录和当前未经排序子序列L.r[1…i]中
//最后一个记录相互交换
HeapAdjust(L,1,i-1);//将H.r[1..i-1]重新调整为大顶堆
⑦菜单程序利用dowhile循环实现对菜单的正确选择,并返回选择的操作代码
intmenu_select()//菜单目录
intnu;
chars;
printf("************************************************\n");
printf("*****菜单*****\n");
printf("1.BubbleSort\n");//冒泡排序
printf("2.insertSort\n");//直接插入排序
printf("3.SelectSort\n");//简单选择排序
printf("4.QSort\n");//查找排序
printf("5.ShellSort\n");//希尔排序
printf("6.HeapSort\n");//堆排序
printf("7.退出程序\n");
printf("请输入数字:
1-7选择相应的排序方式或操作:
\n");
do
s=getchar();
nu=(int)s-48;
}while(nu<0||nu>55);
return(nu);
⑧主函数是程序的入口,包括两个for循环,一个用来实现随机数产生与输出;另一个用来实现菜单的选择的连续;第二个for循环中包含一个switch循环,作用是根据菜单程序的返回值选择相应的操作,随后作相应的修改使待排序数据分别为正序和逆序,以实现本设计的目的。
voidmain()
printf("输出待排序的数据:
");
for(;;)
switch(menu_select())//选择相应的排序操作
case1:
BubbleSort(&A);break;
case2:
insertSort(&B);break;
case3:
SelectSort(&C);break;
case4:
QSort(&L,1,L.length);break;
case5:
ShellSort(&D,dlta,t);break;
case6:
HeapSort(&E);break;
case7:
exit(0);
运行结果:
第一组随机数据及其运行结果如下:
第二组随机数据及其运行结果如下:
第三组随机数据及其运行结果如下:
第四组随机数据及其运行结果如下:
第五组随机数据及其运行结果如下:
第六组正序数据及其运行结果如下:
第七组逆序数据及其运行结果如下:
【小结与讨论】
表中数据为关键字移动次数:
BubbleSort
insertSort
SelectSort
QSort
ShellSort
HeapSort
第一组
6974
2690
605
393
723
1082
第二组
7066
2473
586
382
742
1081
第三组
7072
2733
592
408
724
1075
第四组
7068
2587
583
368
788
1087
第五组
6963
2787
620
402
754
1085
第六组
9900
0
297
1136
第七组
4950
5148
2650
540
1012
下表中数据为关键字比较次数:
2504
638
785
749
2293
635
790
752
2550
600
796
2404
625
824
748
2607
621
804
745
99
480
811
672
662
有数据分析可知:
无论待排序列排序如何,BubbleSort与SelectSort两种算法的关键字比较次数均为4950,即n(n-1)/2次;待排序列为随机序列时直接插入排序的关键字比较次数和关键字移动平均值约为n2/4,正序时比较次数为(n-1),移动次数为0,逆序时为n(n-1)/2次;整体看来六种排序中QSort、ShellSort、HeapSort三种排序比较和移动次数相对较少;但BubbleSort、insertSort两种排序属于稳定性排序(每交换一次纪录,关键字就有三次移动纪录)。
在测试过程中,当连续选择一个操作时,发现有的排序方式的关键字移动次数会随之增加,例如BubbleSort,QSort,HeapSort;仔细分析后得知,无论待排序列是否排好,它每次运行都会有关键字的移动;每次运行程序,六种排序方式的关键字比较次数也会随着操作次数的增加而增加,原因与上一个问题先同。
【程序清单】
#include
#defineMAXSIZE100
typedefintKeyType;//定义关键字的整数类型
/**********************************************************************/
inti,j;
intN=L->length;
for(j=1;j{if(L->r[0].key>=L->r[j+1].key){L->r[j]=L->r[j+1];L->info++;}else{L->r[j]=L->r[0];L->r[0]=L->r[j+1];L->info+=2;}L->cmp++;L->r[j+1]=L->r[0];}}printf("以下数据的排列顺序为BubbleSort排序后的排列顺序:\n");for(i=1;i<=MAXSIZE;i++)printf("%d\t",L->r[i]);//输出排序后数组和相关数据printf("关键字移动次数为:%d\n",L->info);printf("关键字比较次数为:%d\n",L->cmp);}/**********************************************************************/voidinsertSort(Sqlist*L)//直接插入排序{inti,j;for(i=2;i<=L->length;i++){L->cmp++;if((L->r[i].keyr[i-1].key)){L->r[0]=L->r[i];//复制哨兵L->r[i]=L->r[i-1];L->info+=2;for(j=i-2;(L->r[0].keyr[j].key);j--){L->r[j+1]=L->r[j];//纪录后移L->info++;L->cmp++;}L->r[j+1]=L->r[0];//插入到正确位置L->info++;}}printf("以下数据的排列顺序为insertSort排序后的排列顺序:\n");for(i=1;i<=L->length;i++)printf("%d\t",L->r[i]);//输出排序后数组和相关数据printf("关键字移动次数为:%d\n",L->info);printf("关键字比较次数为:%d\n",L->cmp);}/**********************************************************************/voidSelectSort(Sqlist*L)//简单选择排序{inti,j,k;for(i=1;i<=L->length;++i)//选择第i小的记录,并交换到位{L->r[0]=L->r[i];j=i;for(k=i+1;k<=L->length;k++)//在L.r[i..L.length]中选key最小者{L->cmp++;if(L->r[0].key>L->r[k].key){L->r[0]=L->r[k];j=k;L->info++;}}if(i!=j){//L.r[i]←→L.r[j];与第i个记录交换RedTypetemp;temp=L->r[i];L->r[i]=L->r[j];L->r[j]=temp;L->info+=3;}}printf("以下数据的排列顺序为SelectSort排序后的排列顺序:\n");for(i=1;i<=L->length;i++)printf("%d\t",L->r[i]);//输出排序后数组和相关数据printf("关键字移动次数为:%d\n",L->info);printf("关键字比较次数为:%d\n",L->cmp);}/**********************************************************************/intserchSort(Sqlist*L,intlow,inthigh)//查找排序单次排序{intpivotkey;L->r[0]=L->r[low];pivotkey=L->r[low].key;//枢轴纪录关键字while(low{while(lowr[high].key>=pivotkey){high--;L->cmp++;}L->r[low]=L->r[high];//将比枢轴纪录小的纪录移到底端L->info++;while(lowr[low].key<=pivotkey){low++;L->cmp++;}L->r[high]=L->r[low];//将比枢轴纪录大的纪录移到高端L->info++;}L->r[low]=L->r[0];L->info++;returnhigh;}voidQSort(Sqlist*L,intlow,inthigh)//快速排序{intpivotloc;if(low{//长度大于1pivotloc=serchSort(L,low,high);//将L.r[low..high]一分为二QSort(L
L->r[j]=L->r[j+1];
L->info++;
L->r[j]=L->r[0];
L->r[0]=L->r[j+1];
L->info+=2;
L->cmp++;
L->r[j+1]=L->r[0];
printf("以下数据的排列顺序为BubbleSort排序后的排列顺序:
for(i=1;i<=MAXSIZE;i++)
printf("%d\t",L->r[i]);//输出排序后数组和相关数据
printf("关键字移动次数为:
%d\n",L->info);
printf("关键字比较次数为:
%d\n",L->cmp);
printf("以下数据的排列顺序为insertSort排序后的排列顺序:
for(i=1;i<=L->length;i++)
inti,j,k;
for(k=i+1;k<=L->length;k++)//在L.r[i..L.length]中选key最小者
{//L.r[i]←→L.r[j];与第i个记录交换
RedTypetemp;
temp=L->r[i];
L->r[i]=L->r[j];
L->r[j]=temp;
L->info+=3;
printf("以下数据的排列顺序为SelectSort排序后的排列顺序:
intserchSort(Sqlist*L,intlow,inthigh)//查找排序单次排序
intpivotkey;
L->r[0]=L->r[low];
while(low{while(lowr[high].key>=pivotkey){high--;L->cmp++;}L->r[low]=L->r[high];//将比枢轴纪录小的纪录移到底端L->info++;while(lowr[low].key<=pivotkey){low++;L->cmp++;}L->r[high]=L->r[low];//将比枢轴纪录大的纪录移到高端L->info++;}L->r[low]=L->r[0];L->info++;returnhigh;}voidQSort(Sqlist*L,intlow,inthigh)//快速排序{intpivotloc;if(low{//长度大于1pivotloc=serchSort(L,low,high);//将L.r[low..high]一分为二QSort(L
while(lowr[high].key>=pivotkey)
high--;
L->r[low]=L->r[high];//将比枢轴纪录小的纪录移到底端
while(lowr[low].key<=pivotkey)
low++;
L->r[high]=L->r[low];//将比枢轴纪录大的纪录移到高端
L->r[low]=L->r[0];
returnhigh;
intpivotloc;
if(low{//长度大于1pivotloc=serchSort(L,low,high);//将L.r[low..high]一分为二QSort(L
pivotloc=serchSort(L,low,high);//将L.r[low..high]一分为二
QSort(L
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1