掌握各种内排序算法设计及其执行绝对时间,并对其时间性能进行比较。
内容:编写一个程序,随机产生n个1-99的正整数序列,分别采用直接插入排序、折半插入排序、希尔排序、冒泡排序、快速排序、简单选择排序、堆排序和二路归并排序算法对其递增排序,求出每种排序方法所需要的绝对时间。
要求:为了便于体现各类排序算法的执行时间差别,要求产生不少于50000个随机数进行测试,同时需要编写测试函数用于测试排序结果是否为递增的。
2-1各种排序算法及性能分析功能图 |
本次的程序是一个对各种算法进行统计的集合,需要编写各种排序算法,在程序中需要有生成随机数的initial()函数、进行位置交换的函数swap()以及验证其正确性的test()函数、对数据及堆排序数据复制的copy()以及copy1()函数。而最为关键的则是实验中涉及算法的函数,包括了InsertSort()、BinInsertSort()、 ShellSort()、BubbleSort()、GBubbleSort()、partition()、QuickSort()、SelectSort()、sift()、HeapSort()、Merge()、MergePass()和MergeSort()函数的编写,在每种排序后有对应的计算时间所用函数,该程序的效果如图2-1各种排序算法及性能分析功能图所示。
在这些排序算法中性能最好的两种为堆排序和二路归并排序,其中堆排序是通过将无序表转化为堆,可以直接找到表中最大值或者最小值,然后将其提取出来,令剩余的记录再重建一个堆,取出次大值或者次小值,如此反复执行就可以得到一个有序序列。二路归并排序是分为两个基本的操作,一个是分,也就是把原数组划分成两个子数组的过程。另一个是治,它将两个有序数组合并成一个更大的有序数组。在归并的过程中将子表两两合并,每合并一次,就会产生一个新的且更长的有序表,重复这一步骤,直到最后只剩下一个子表,这个子表就是排好序的线性表。
堆排序通过循环对堆的元素进行排列来构造大根堆或是小根堆,该过程如图2-3所示
2-3大根堆的构建
堆排序通过两个函数HeapSort()和sift()来实现排序的功能,其中HeapSort()通过循环调用sift()来实现大根堆的构建和最大元素的交换。Sift()函数会通过比较孩子和双亲的大小来构建大根堆,并且会向下继续判断来保证后面的孩子不会被破坏大根堆的结构。
2-4二路归并排序过程 |
二路归并排序通过Merge()、MergePass()和MergeSort()三个函数来实现,其中MergeSort()函数循环调用MergePass()函数,然后MergePass()函数在循环调用Merge()函数来实现划分和合并的操作。其中对数组的具体操作过程如图2-4所示,先将数组划分为单个元素,然后再两两合并。
2-3运行结果
对于运行结果进行来看,冒泡排序用时最长,改进冒泡排序相对于冒泡排序来说性能有很大的提升,堆排序用时最短。堆排序的效率与快排、归并相同,都达到了基于比较的排序算法效率的峰值(时间复杂度为O (nlogn))而除了高效之外,还只需要O (1)的辅助空间,既最高效率又最节省空间。
对于这次实验我写了直接插入排序、折半插入排序、希尔排序、冒泡排序、改进冒泡排序、快速排序、简单选择排序、堆排序和二路归并排序算法。在这么多排序算法里面我所熟知的只有冒泡排序这一个,并且我发现冒泡排序的性能要远远落后于其他排序算法。并且其中快速排序,堆排序,二路归并排序,都用到了递归的操作,并且递归算法我并不是很熟悉。这也导致了我对这些排序算法很难理解,但最终结果是好的,我学会了更多新的排序算法,还意识到算法的性能和效率对于编程的重要性,也明白了我的代码能力还很薄弱。
这些新颖的排序算法极大的开阔了我的思路,也为我以后的代码提供了更多的选择。在比较各种排序算法时,我注意到了时间复杂度和空间复杂度的重要性。不同的算法在这些方面有着显著差异,这直接影响着它们的实际应用场景。例如,快速排序和堆排序虽然在某些情况下比冒泡排序快得多,但它们的空间复杂度较高,可能会对资源有限的系统造成压力。对于一些简单的排序算法,如冒泡排序,时间复杂度为O(n^2),意味着随着数据规模的增加,算法的运行时间会急剧增长。而快速排序和堆排序的时间复杂度为O(nlogn),在许多情况下性能更优。
此外,我也意识到了算法的可读性和可维护性。虽然一些算法在性能上可能更优秀,但如果它们的代码难以理解和维护,那么在长期的项目开发中可能会带来问题。因此,在选择排序算法时,需要综合考虑多个因素。
#include
#include
#include
#define MaxSize 50001
void swap(int &x,int &y)//交换
{
int tmp=x;
x=y;
y=tmp;
}
void initial(int R[],int low,int high)//初始化产生50001个随机数
{
int i;
srand((unsigned)time(NULL));
for(i=low;iR[i+1]){
printf("\t 错误\n");
return ;
}
}
printf("\t 正确\n");
}
void InsertSort(int R[],int n)//直接插入排序
{
int i,j;
int tmp;
for(i=1;i=0&&R[j]>tmp);
R[j+1]=tmp;
}
}
}
void BinInsertSort(int R[],int n)//折半插入排序,无序区中元素插到有序区里
{
int i,j,low,high,mid;
int tmp;
for(i=1;i=high+1;j--)//元素后移
R[j+1]=R[j];
R[high+1]=tmp;
}
}
}
void ShellSort(int R[],int n)//希尔排序,每间隔d相比较,反序时交换顺序
{
int i,j,d;
int tmp;
d=n/2;
while(d>0)
{
for(i=d;i=0&&tmpi;j--)
{
if(R[j]i;j--)
{
if(R[j]i&&R[j]>=tmp)
j--; //从右往左扫描,找一个小于tmp的
R[i]=R[j];//找到的R[j]放到R[i]处
while(i=1;i--)
{
sift(R,i,n);
}
for(i=n;i>=2;i--)
{
swap(R[1],R[i]);//将最后一个元素与跟R[1]交换
sift(R,1,i-1);
}
}
void Merge(int R[],int low,int mid,int high)
{
int *R1;
int i=low,j=mid+1,k=0;
R1=(int *)malloc((high-low+1)*sizeof(int));
while(i<=mid&&j<=high)
{
if(R[i]<=R[j])
{
R1[k]=R[i];
i++;
k++;
}
else
{
R1[k]=R[j];
j++;
k++;
}
}
while(i<=mid)
{
R1[k]=R[i];
i++;
k++;
}
while(j<=high)
{
R1[k]=R[j];
j++;
k++;
}
for(k=0,i=low;i<=high;k++,i++)
R[i]=R1[k];
free(R1);
}
void MergePass(int R[],int length,int n)
{
int i;
for(i=0;i+2*length-1