数据结构课程设计--内部排序算法的比较
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
数据结构课程设计
题目:内部排序算法的比较
姓名:李吉倩
学号:020*********
系年级:计算机科学与技术2010级
完成时间:2012.8-2012.9
实验报告
一、需求分析
1.本程序对以下六种常用的内部排序进行实测比较:起泡排序、直接插入
排序、简单选择排序、快速排序、希尔排序、堆排序。
2.待排序表的元素的关键字为整数,雍正徐、逆序和随机数产生器产生的随机数做测试比较。
比较的指标为有关键字参加的比较次数和关键字的移动次数(关键字交换记为3次移动)。
3.程序以用户和计算机的对话方式执行,即在计算机终端上显示“提示信息”下,用户可由键盘输入产生随机数的种子,计算机终端显示各内部排序的比较参数。
4.最终对结果做出简单分析,包括对各组数据得出结果波动大小给予解释。
二、概要设计
1.可排序表的抽象数据类型定义:
ADT OrderableList
{
数据对象:D = {a
i |a
i
∈IntegerSet,i = 1,2,……,n,n >= 0}
数据关系:R1 = {<a
i-1,a
i
>|a
i-1
,a
i
∈D.i = 2,……,n}
基本操作:
SelectListType(c)
操作结果:打印提示信息,请用户选择待排序表的类型,顺序型、
逆序型还是随机数组。
BubbleSort(&L)
操作结果:进行起泡排序,返回排序后的顺序表
InsertSort(&L)
操作结果:进行插入排序,返回排序后的顺序表
SelectSort(&L)
操作结果:进行选择排序,返回排序后的顺序表
QuickSort(&L)
操作结果:进行快速排序,返回排序后的顺序表
ShellSort(&L)
操作结果:进行希尔排序,返回排序后的顺序表
HeapSort(&L)
操作结果:进行堆排序,返回排序后的顺序表
SelectSortMethod(&L,c1)
操作结果:打印提示信息,请用户选择排序方法,起泡排序、插入
排序、选择排序、快速排序、希尔排序还是堆排序
OutPut(L)
操作结果:打印排序中关键字的比较次数和移动次数
}ADT OrderableList
2.本程序包含两个模块:
1).主程序模块
int mian()
{
初始化;
用户选择待测表的类型;
输入选择,用switch语句判断;
While(“命令”!= “退出”)
{
接受命令;
处理命令;
}
}
2).可排序表单元模块----实现可排序表的抽象数据类型
各模块之间的调用关系:
主程序模块
可排序表单元模块
三、详细设计
1.根据题目要求何可排序表的基本操作特点,可排序表采用证书顺序表存储结构,并实现在头文件SqList.H。
//******************基本操作的函数原型******************\\
#define MAXSIZE 20 //用作示例的顺序表的最大长度typedef int KeyType; //定义关键字为整数类型
typedef int InfoType; //定义其他数据项也为整数类型int time_key_compare = 0; //定义全局变量,关键字比较次int time_key_move = 0; //数和移动次数均为0
typedef struct
{
KeyType key; //关键字项
InfoType otherinfo; //其他数据项
}RedType; //记录类型
typedef struct
{
RedType r[MAXSIZE + 1]; //r[0]闲置或用做哨兵
int length; //顺序表长度
}SqList; //顺序表类型
//****************************基本操作*********************\\ bool LT(KeyType a,KeyType b)//比较两个KeyType类型变量的大小{
time_key_compare ++; //比较次数+1
if(a < b)
return true; //<,返回true else
return false; //否则,返回false
void copyKey(RedType &a,RedType &b)
{ //将RedType类型变量b复制给a
a = b;
time_key_move ++; //关键字移动次数+1 }//copyKey
void swap(RedType &a,RedType &b)
{ //将RedType型变量a和b进行交换操作RedType c; //定义RedType型变量c
c = a;
a = b;
b = c;
time_key_move += 3; //关键字移动次数+3 }//swap
/****************直接插入排序*********************\
void InsertSort(SqList &L)
{ //对顺序表L做直接插入排序for(int i = 2; i <= L.length; ++i)
{
if(LT(L.r[i].key,L.r[i - 1].key))
{ //“<”,需将L.r[i]插入有序子表copyKey(L.r[0] ,L.r[i]); //复制为哨兵
copyKey(L.r[i] ,L.r[i - 1]);
for(int j = i - 2;LT(L.r[0].key,L.r[j].key); --j)
{
copyKey(L.r[j + 1] ,L.r[j]); //记录后移
}
copyKey(L.r[j + 1] ,L.r[0]); //插入到正确的位置}
}
}//InsertSort
//*************************希尔排序*************************\ void shellInsert(SqList &L,int dk)
{ //对顺序表L做一希尔插入排序。
//1.前后记录位置的增量式是dk,而不是1;
//2.r[0]只是暂存单元,不是哨兵。
当 j<= 0时,插入位置已找到for(int i = dk + 1;i <= L.length; ++i)
{
if(LT(L.r[i].key,L.r[i - dk].key))
{ //需将L.r[i]插入到有序增量子表copyKey(L.r[0] ,L.r[i]); //暂存在L.r[0]中
for(int j = i - dk;j > 0 && LT(L.r[0].key,L.r[j].key); j-= dk)
copyKey(L.r[j + dk] ,L.r[j]); //记录后移
copyKey(L.r[j + dk] ,L.r[0]); //插入
}
}//ShellInsert
void ShellSort(SqList &L,int dlta[],int t)
{ //按增量序列dlta[0……t - 1]对顺序表L作希尔排序for(int k = 0;k < t; ++k)
shellInsert(L,dlta[k]); //一趟增量为dlta[k]的插入排序}//ShellSort
//**************起泡排序****************************\
void BublleSort(SqList &L)
{
int i = L.length;
while(i > 1)
{
int lastExchangeIndex = 1;
for(int j = 1;j < i ;j ++)
{
if(LT(L.r[j + 1].key,L.r[j].key)) //小于成立进行交换
{
swap(L.r[j + 1],L.r[j]);
lastExchangeIndex = j;
}
}
i = lastExchangeIndex;
}
}//BubbleSort
/***************************简单选择排序********************/ int SelectMinKey(SqList L,int i)
{ //在L.r[i..L.length]中选择key最小的记录 for(int j = i ;j <= L.length; ++j)
{
if(LT(L.r[j].key,L.r[i].key))
{
i = j;
}
}
return i; //返回最小记录下标}
void SelectSort(SqList &L)
{ //对顺序表做简单选择排序for(int i = 1;i < L.length; ++i)
{ //选择第i小的记录,并交换到位int j = SelectMinKey(L,i);
//令j为L.r[i..L.length]中选择key最小的记录
if(i != j)
{
swap(L.r[j],L.r[i]); //与第i记录进行交换}
}
}//SelecteSort
/*********************快速排序*************/
int Partition(SqList &L,int low,int high)
{ //交换顺序表L中字表r[low……high]的记录,枢轴记录到位,并
//返回其所在位置,此时在他之前(后)的记录均不大(小)与它。
copyKey(L.r[0],L.r[low]);//用字表的第一个纪录作枢轴记录
KeyTypepivotkey=L.r[low].key; //枢轴记录关键字
while(low < high)
{ //从表的两端交替向中间扫描while(low<high&& !LT(L.r[high].key,pivotkey)) --high;
copyKey(L.r[low],L.r[high]);
//将比枢轴记录小的记录移到低端while(low<high && !LT(pivotkey,L.r[low].key)) ++low; copyKey(L.r[high],L.r[low]);
//将比枢轴记录大的记录移到高端}
copyKey(L.r[low],L.r[0]); //枢轴记录到位return low; //返回枢轴位置}//Partition
void QSort(SqList &L,int low,int high)
{ //对顺序表L中的子序列L.r[low……high]做快速排序if(low < high)
{ //长度> 1 int pivotloc = Partition(L,low,high);
//将L.r[low……high]一分为二QSort(L,low,pivotloc - 1);
//对低子表递归排序,pivotloc是枢轴位置QSort(L,pivotloc + 1,high);
//对高子表递归排序}
}//QSort
void QuickSort(SqList &L)
{ //对顺序表L做快速排序QSort(L,1,L.length);
}//QuickSort
/*************************堆排序*********************/
void HeapAdjust(SqList &L,int s,int m)
{ //已知L.r[s...m]中记录的关键字除L.r[s].key之外均满足定义,
//本函数调整H.r[s]的关键字,使H.r[s...m]成为一个大顶对
RedType rc = L.r[s];
for(int j = 2 * s;j <= m;j *= 2)
{ //沿key较大的孩子结点向下帅选if(j < m && LT(L.r[j].key,L.r[j+1].key)) ++j;
//j 为key较大的记录的下标if(!LT(rc.key,L.r[j].key)) break;//rc应插入在位置s上 copyKey(L.r[s],L.r[j]);
s = j;
}
L.r[s] = rc; //插入}//HeapAdjust
void HeapSort(SqList &L)
{//对顺序表L进行堆排序
for(int i=L.length/2;i>0;--i)
//把L.r[1....H.length]建成大顶堆HeapAdjust(L,i,L.length);
for( i = L.length;i >1; --i)
{
swap(L.r[1],L.r[i]); //将堆顶记录和当前未经排序的子序列 // L.r[1……i]中最后一个记录进行交换HeapAdjust(L,1,i - 1);//将L.r[1....i-1]重新调整为大顶堆}
}//HeapSort
2.主函数和其他辅助函数的伪码算法
#include<stdlib.h> //产生随机数所需的库函数
/*****************************主函数************************/ int main()
{
int arry[MAXSIZE + 1]; //存放排序前数组
int typeChoice; //根据提示键入的类型选择序号 int methodChoice; //根据提示键入的方法选择序号 int seed; //用于产生随机数的种子
PrintArrayMenu(); //打印选择待排序表的类型菜单 switch(typeChoice)
{
case 1:选择的是顺序表,初始化链表并打印
break;
case 2:选择的是逆序表,初始化链表并打印
break;
case 3:选择的是随机数表
srand(seed);
//初始化随机数,并利用rand()产生随机数并打印break;
default:
}
cout <<L.length; //打印待排序数组的长度 PrintMenu(); //打印排序方法选择菜单 while(methodChoice!= 7)
{
getChoice(L,choice);
//根据选择的方法堆表进行排序操作并打印相关信息 for( j = 1;j <= MAXSIZE; j ++)
{ //将链表恢复至未排序时状态
L.r[j].key = arry[j];
}
}
return 0;
}
/****************根据选择排序********************/
void getChoice(SqList &L,int c)
{ //根据选择进行排序并输出相关信息 int Array[3] = {5,3,1}; //希尔排序所需增量数组 switch(c)
{
case 1:
InsertSort(L); // 直接插入排序
outPut(L); //输出关键字比较次数和移动次数
break;
case 2:
ShellSort(L,Array,3); //希尔排序
outPut(L); //输出关键字比较次数和移动次数
break;
case 3:
BublleSort(L); //起泡排序
outPut(L); //输出关键字比较次数和移动次数
break;
case 4:
SelectSort(L); //简单选择排序
outPut(L); //输出关键字比较次数和移动次数
break;
case 5:
QuickSort(L); //快速排序
outPut(L); //输出关键字比较次数和移动次数
break;
case 6:
HeapSort(L); //堆排序
outPut(L); //输出关键字比较次数和移动次数
case 7:排序结束 default: break; }
}
3.函数的调用关系图反映了程序的层次结构
InsertSort
顺序表 ShellSort
BublleSort 逆序表 getTypeChoice getMethodChoice SelectSort
随机数表 QuickSort
HeapSort srand rand
四、排序算法结果比较
测试 直接插入排序 希尔排序 起泡排序 简单选择排序 快速排序 堆排序 表长 顺序表 19/0 51/0 19/0 209/0 190/76 121/118 20 逆序表 209/228 110/108 190/570 209/30 200/76 105/100 20 随即表1 134/147 105/85 184/345 209/51 105/76 115/109 20 随即表2 106/119 119/101 174/261 209/45 114/72 118/109 20 随即表3 99/108 107/84 165/240 209/42 107/74 119/118 20 随即表4 123/134 117/98 176/312 209/48 104/82 109/106 20 随即表5 140/153 105/85 189/363 209/51 119/72 107/101 20 随即表6 24962887/24972872
5153988/5146226 49981281/74858664 50004999/29976 226064/75760 235476/144261 10000 随即表7 25067619/25077602
5161759/5153799 49952164/75172860 50004999/29964 225821/76130 235408/144248 10000 随即
24951520/2496
5183866/51
49904576/748
50004999/2
223755/758
235241/144
1000
主程序
表8 1493 76131 24563 9970 18 179 0
随即表9 25231354/2524
1327
5240599/52
32732
49959846/756
64065
50004999/2
9982
216797/757
86
235488/144
253
1000
随即表10
24940230/2495
0219
5166043/51
58198
49952854/747
90693
50004999/2
9967
210437/768
44
235432/144
304
1000
0 对各种表长和测试组数进行了测试,程序运行正常。
分析实测得到的数值,
六种排序算法的特点小结如下:
测试直接插入排序希尔排序起泡排序简单选择排序快速排序堆排序
比较次数
第三多
越乱越多
第三少
乱否差异小
第二多
越乱越多
最多
与乱否无关
最少
乱否差异小
第二少
乱否差异小
移动次数
第二多
越乱越多
第三多
越乱越多
最多
越乱越多
最少
乱否差异小
第二少
乱否差异小
第三少
乱否差异小
五、附录
源程序文件名清单:
SqList.H //可排序表的实现
main.C //主程序
六、实验中遇到的问题及解决办法
1.程序采取循序渐进的策略。
首先设计和实现可排序表的建立操作,根据要求因包括随机数序列、正序和逆序三种情况,为避免不必要的错误,程序采用了先定义了两个数组,分别为正序和逆序,程序运行时分别将数组的值赋给链表的方式进行顺序表的建立,另外,起初对于随机数的产生产生了疑惑,但随后通过搜集资料成功的使用srand(seed)和rand()函数得到解决。
然后用插入排序验证了各种内部辅助操作的正确性,进而逐个加入其他排序算法,最后完成对测试结果的显示。
2.程序旨在进行六种排序方法优劣性的比较,在进行对六种排序依次排序时,出现了进行过依次排序后,排序表变为正序表而之后的集中排序方法都是在进行正序表的排序,而没有达到想要的效果,最后在主函数中添加了一个数组,用于记录原排序表的顺序,并在每次排序之后利用该数组对排序表进行再次建立,使得问题成功解决。
3.开始编程之初,总会出现关键字比较和移动次数的计算错误,在程序中进行逐步记录既繁琐又易出错,之后经过思考,建立了copyKey()和swap()函数,成功提高了程序效率并降低了计算错误率。