这是中科大2021年春季课程《算法分析与设计》的课程实验1
实验题目
结果+唠嗑
由于实验要求,本次实验采用的四种排序算法分别是:插入O(n2),归并O(nlg(n)),堆排序O(nlg(n)),以及喜闻乐见的快速排序O(nlg(n))。考虑到设计运行时间的比较,所以采用了快且稳定的C语言。(其实我真的没的选,除了C只会python)
所有数据都是随机乱序生成的,都说快排是系数相对小的nlgn算法,实验结果真的如此吗?先上结果图:
方差为什么波动这么大我也不知道,非常不幸的是插入排序如果放进图里,其他三条曲线全部都得趴在地上,别说抬头了,连个波动都不会有。这就是来自数量级的时间压制。
运行数据我都附在后面的exel文档里,其实我对每个运行结果都进行了截图,不过由于我的程序没怎么考虑输出的交互,所以有些丑,我就不贴了。数据处理用的也是这个exel软件。感兴趣的可以下载查看,链接我之后贴在评论区吧,私信我也可以。
排序的原理我也不说了,值得一提的是这个时间的统计方式,我调用了C的系统库time.h,利用了其中的计时工具来记录每一次排序所消耗的时间,然后我就可以直接一次运行10组记录数据嘿嘿。是不是方便了很多呢,其实并没有,4种排序算法,每个10组,每个跑10次,400个数据对我来说其实统计维护也挺麻烦的,出于结果考虑,有的情况明显可能是CPU调度之类的其他莫名其妙原因产生了比较大的系统偏差,我就进行了第二、三次重跑。
基本可以确定,对于随机生成的无序数组,在数据量相对比较大的时候(5万-50万范围)排序所有元素运行时间,有
归并>堆>快排
代码
好啦,主要也就是这个结论啦,代码双手奉上:
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#define MAXINT 2147483647
#define ARRAY 300000
//生成多大的数组
#define RANGE 10000000
//产生随机数的最大值
void BuildArray (long *p,long size){
long i;
srand((unsigned) time(NULL)); //用时间做种,每次产生随机数不一样
// printf("size=%d\t",size);
for (i = 0; i < size; i ++){
p[i] = (rand()*32767+rand()) % RANGE + 1; //产生1-RANGE的随机数
// printf("%ld ",p[i]);
}
}
void swap(long *a, long *b){
int temp;
temp = *a;
*a = *b;
*b = temp;
}
void InsertionSort(long *arr, long size){
long i, j, tmp;
for (i = 1; i < size; i++) {
if (arr[i] < arr[i-1]) {
tmp = arr[i];
for (j = i - 1; j >= 0 && arr[j] > tmp; j--) {
arr[j+1] = arr[j];
}
arr[j+1] = tmp;
}
}
}
void Heapify(long *arr,long m,long size){
long i, tmp;
tmp = arr[m];
for (i = 2 * m; i <= size; i *= 2) {
if (i + 1 <= size && arr[i] < arr[i+1]) {
i++;
}
if (arr[i] < tmp) {
break;
}
arr[m] = arr[i];
m = i;
}
arr[m] = tmp;
}
void Merge(long *num,long start,long mid, int end){
long *temp = (long *)malloc((end-start+1) * sizeof(long)); //申请空间来存放两个有序区归并后的临时区域
long i = start;
long j = mid + 1;
long k = 0;
while (i <= mid && j <= end){
if (num[i] <= num[j]){
temp[k++] = num[i++];
} else {
temp[k++] = num[j++];
}
}
while (i <= mid){
temp[k++] = num[i++];
}
while (j <= end){
temp[k++] = num[j++];
}
//将临时区域中排序后的元素,整合到原数组中
for (i = 0; i < k; i++){
num[start + i] = temp[i];
}
free(temp);
}
void MergeSort(long *num, long start,long end){
long mid = start + (end - start) / 2;
if (start >= end){
return;
}
MergeSort(num, start, mid);
MergeSort(num, mid + 1, end);
Merge(num, start, mid, end);
}
void BuildHeap(long A[],long i,long N){
long child;
long Tmp=A[i];
for ( ; 2*i+1 < N; i = child){
child = 2*i+1; //左孩子是2*i+1
if (child != N - 1 && A[child + 1] > A[child])
child++; //找到较大的儿子节点与父亲比较
if (Tmp < A[child])
A[i] = A[child];
else
break;
}
A[i] = Tmp;
}
void HeapSort(long A[], long N){
long i;
for (i = N / 2; i >= 0; --i)
BuildHeap(A, i, N); //构造堆
for(i=N-1;i>0;--i){
swap(&A[0],&A[i]); //将最大元素(根)与数组末尾元素交换,从而删除最大元素,重新构造堆
BuildHeap(A, 0, i);
}
}
void QuickSort(long *arr, long maxlen, long begin, long end){
long i, j;
if (begin < end) {
i = begin + 1;
j = end;
while (i < j) {
if(arr[i] > arr[begin]){
swap(&arr[i], &arr[j]);
j--;
} else {
i++;
}
}
if (arr[i] >= arr[begin]){
i--;
}
swap(&arr[begin], &arr[i]);
QuickSort(arr, maxlen, begin, i);
QuickSort(arr, maxlen, j, end);
}
}
void putout(long *arr,long size){
for (int i=0;i<size;i++) printf("%ld\n",arr[i]);
putchar('\n');
}
int main( ){
long size = ARRAY;
long arr[size];
int i;
double a[10],sum=0.0;
clock_t start[10],end[10];
printf("本次数据量大小为%ld,算法为归并排序:\n",size);
for (i=0;i<10;i++){
BuildArray(arr,size);
start[i] =clock();//or time(&start);
// InsertionSort(arr, size);
// HeapSort(arr, size);
// QuickSort(arr, size, 0, size-1);
MergeSort(arr,0,size-1);
end[i] =clock();
a[i]=((double)end[i]-start[i])/CLK_TCK;
printf("a[%d]=%.4f\n",i,a[i]);
sum+=a[i];
}
printf("sum=%.4f\n",sum);
// putout(arr,size);
}
代码随意取用,如果能点个赞或者关注我会更加乐意继续分享其他代码~
当然,如果对我的代码有什么优化建议可以在评论区留言或者私信发给我,那么这篇就先写到这里~