前言
记录下本学期的数据结构实验
本实验主要集中于比较几种内部排序算法
提示:以下是本篇文章正文内容,下面案例可供参考
一、问题描述
设计一个程序,对各种内部排序算法的关键字比较次数和移动次数进行比较。具体要求如下:
⑴ 编程实现内部排序算法:编程实现直接插入排序,希尔排序,冒泡排序,快速排序,简单选择排序,堆排序,归并排序算法。
⑵ 要求数据规模:待排序数据类型不限(整型或浮点型),读取自磁盘文件。需用多组、多规模数据进行测试并记录实验结果。例如,数据规模为:{50000,100000,250000,500000,750000};对每组数据规模,建议至少生成5组不同的数据进行测试,以5组数据的性能平均值作为测试结果。
⑶ 评价排序的指标有:在表长相同的情况下,各种排序算法的关键字比较次数、关键字移动次数(关键字交换记为3次移动)、排序时间、排序算法的稳定性;当改变表长时,各种排序算法的性能变化情况,
⑷ 结果记录及分析:在报告中以图和表的形式记录测试结果。例如(以下表格形式仅供参考)。对不同排序算法的性能差异进行分析;提出针对不同的数据集,应该如何选择内部排序算法的建议。
⑸ 建议测试你的系统所能实现内部排序算法的数据规模上限,并针对这一问题,在报告中提出如何解决超大规模数据排序问题的思路。
二、问题分析
1.数据存储结构
如图1,定义结构体NODE,结构体内含有主关键字key,用于存储数据。
2.排序中可能面临的难点问题
(1)快速排序、归并排序的程序编写。归并排序使用的数组除了中间要合并用的是静态数组,其他都是动态数组创建的,堆创建数组本来是挺好的,可是这里却因为数据规模较大的时候,递归太深,只能用数组;快速排序对堆栈要求高,需要的堆栈太大,需要调整堆栈。
(2)交换次数的计数。题目中要求:关键字交换记为3次移动。由于没有清晰这个概念,导致后面的数据均有问题,重新编写代码并填写数据。其中,归并排序并没有交换元素,一开始我是置零处理的,但是后来发现了上述的乌龙,个人认为这里的交换次数其实是指移动次数,因此再次更改了代码和数据。
3.如何观察稳定性
本次实验的数据规模均较大,拟采用另外数据规模为20的数据组,通过debug的方式,进行分析各排序方法的稳定性。
4.数据规模的选择
在正式进行本实验之前,预先测试过不同数据规模下,各排序方法的性能情况。实验中发现,数据规模的选择不宜过小,否则排序时间太短,不利于分析比较各排序方法之间的差别;同时,数据规模的选择也不宜过大,否则排序时间太长,实验的时间会过于太长。
三、实验结果及分析
(1)实验数据描述
①数据规模:本实验的数据规模为1w、2.5w、5w、10w,为四组数据。
②磁盘文件存储格式:.txt格式。文件存储内容如图2所示。
③数据生成方法:
本实验采用随机数生成法生成数据集。首先利用rand()函数生成指定数目的随机数存入data数组,再将数组中的元素存储到DataSet_50000.txt文件中,再通过磁盘读入函数load()从文件中读入数据并存入data数组,最后进行实验测试。具体数据见图2所示。具体实现函数如图3、4所示。
(2)实验结果
实验结果如图5、6所示。
本次实验测试了数据规模分别为1w、2.5w、5w、10w的排序情况,为了数据的稳定,每种数据规模各分为5组进行测试取平均值。
需要注意的是,归并排序原理上是将两个无序的数组合并成一个有序的数组的递归函数,并没有进行数据交换,这里的交换次数仅表示数据移动的概念。
(3)性能分析
①表格分析:
(1)由上面4张表格可以看出,随着数据规模增大,各种排序方法耗时均相应增加,交换次数和比较次数也逐渐增加。
(2)由表格数据可以看出,较快的排序算法有快速排序、堆排序、归并排序、希尔排序;较慢的排序有冒泡排序、直接插入排序、选择排序。
(3)其中我对直接插入算法中,带监视哨和不带监视哨的情况进行了测试,发现带监视哨比不带监视哨的排序时间普遍较短,比较次数也相对较少。
(4)相同数据规模下,选择排序法的比较次数相同,归并排序算法的交换次数相同。
②实验数据统计图表分析
A.比较不同排序方法的比较次数和交换次数之间的关系
ⅰ.由上图图7可知,不同排序方法,比较次数和交换次数有较大的差别。
ⅱ.其中,选择排序、直接选择排序、冒泡排序、不带监视哨的直接插入排序这四种排序方法,他们的比较次数均大于交换次数。而带监视哨的直接插入排序的比较次数和交换次数相差较小,可以忽略不计。
ⅲ.但是,快速排序、堆排序、归并排序和希尔排序此处仅能看出与其他排序方法的比较次数和交换次数的差别,即远小于其他排序方法的比较次数和交换次数,不能够比较内部的比较次数和交换次数之间的关系。因此,在去掉选择排序、直接选择排序、冒泡排序、不带监视哨的直接插入排序、带监视哨的直接插入排序这五种排序方法的干扰后,重新比较了快速排序、堆排序、归并排序和希尔排序这四种排序方法的比较次数和交换次数之间的关系。具体如下图图8所示。
ⅳ.由图8可以看出,快速排序、归并排序和希尔排序,他们的比较次数均大于交换次数,而堆排序的比较次数和归并次数相差较小。
ⅴ.由以上分析可以得知,不同排序方法在相同数据测试的情况下,得到的数据相差较大,在使用图表进行分析时,会导致部分数据被较大的掩盖了其本身的重要关系,进而影响分析的准确性和合理性。因此,根据分析之前得到的图表数据,我决定将这九组分为复杂排序和简单排序两类,即为
复杂排序类:选择排序、直接选择排序、冒泡排序、
不带监视哨的直接插入排序、带监视哨的直接插入排序
简单排序类:快速排序、堆排序、归并排序、希尔排序
B.比较不同数据规模下,不同排序方法之间的比较次数关系
四、源代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define N 25000 //元素最大个数
typedef struct {
int key; //主关键字
} NODE;
void creatfile(NODE data[], int *n);//创建磁盘文件
void load(NODE data[], int n);//导入磁盘文件中的信息
void prn(NODE data[], int begin, int n);//输出随机数组begin..n-1
int partition(NODE data[], int low, int high);
void quicksort(NODE data[], int low, int high);//快速排序算法
void selesort(NODE data[], int n);//选择排序
void dselesort(NODE data[], int n);//直接选择排序
void pasort(NODE data[], int n);//冒泡排序
void dpasort(NODE data[], int n);//改进的冒泡排序
void sift(NODE data[], int i, int j);
void creatheap(NODE data[], int n);//堆排序,建立大根堆
void heapsortt(NODE *data, int n);//堆排序算法
void merge(NODE data[], int low, int m, int high);//归并
void mergepass(NODE data[], int n, int length);//一趟归并算法
void mergesortt(NODE *data, int n);//自底向上的二路归并排序算法
void inser(NODE data[], int n);//直接插入排序(不带监视哨)
void sinser(NODE data[], int n);//直接插入排序(带监视哨)
void shellsort(NODE data[], int n);//希尔排序算法
void Menu(void);
long exchange_time, compare_time;
void main() {
NODE DATA[N];
int n, op;
clock_t start, finish;
double total_time;
Menu();
scanf("%d", &op);
while (op != 20) {
switch (op) {
case 1:
creatfile(DATA, &n);
break;
case 2:
load(DATA, n);
break;
case 3:
prn(DATA, 0, n);
break;
case 4:
exchange_time = 0;
compare_time = 0;
printf("\t\t4.快速排序算法\n");
load(DATA, n);
start = clock();
quicksort(DATA, 0, n - 1);
finish = clock();
prn(DATA, 0, n);
total_time = (double) (finish - start) / CLOCKS_PER_SEC;
printf("比较次数:%ld\n", compare_time);printf("交换次数:%ld\t", exchange_time);
printf("运行时间:%lf s\n", total_time);
break;
case 5:
exchange_time = 0