一、实验内容及目的
实验内容:
1.快速排序
内容:
快速排序通过采用分而治之策略将列表划分为两个子列表来进行排序,用归并排序法对此题进行解答,对一组整数数列A[1],A[2],A[3]…A[N]进行排序,按照从小到大的顺序输出。输入第一行一个N,表示数组的大小,接下来在N行输入N个整数,每个用空格分开;对已经排好序的数从小到大依次输出,每两个数之间用一个空格隔开。
2.合并排序
内容:
用归并排序法对此题进行解答,对一组整数数列A[1],A[2],A[3]…A[N]进行排序,按照从小到大的顺序输出。输入第一行一个N,表示数组的大小,接下来在N行输入N个整数,每行一个整数;对已经排好序的数从小到大依次输出,每两个数之间用两个空格隔开,且每输出10个数换行
实验目的:针对实验题目中的问题,选择分治或变治的算法设计策略进行算法设计,使用高级程序设计语言进行编码和调试,并进行效率分析。
二、实验方案
1.快速排序
① 分析:
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
② 算法伪代码:
2.合并排序
① 分析:
合并排序是将一个无序的序列分组,直至分为每两个元素一组(如果有单个元素剩余,则可以剩余的单个元素自己一组),小组内排序,然后合并成一个有序的序列。
算法思想:根据分治法的思想,将这组规模为n的数分解成两组规模为n/2的数,如果子数组中只有一个数则直接返回,否则继续对子数组进行合并排序。排好序后用一个合并函数将两组数合并即可。
合并排序过程
② 算法伪代码
三、结果及分析
1.快速排序
① 测试数据
② 实验结果
③ 时间复杂度:
最坏情况下:
待排序的序列为正序或者逆序,每次划分只得到一个比上一次划分少一个记录的子序列。如果递归树画出来,它就是一棵斜树。此时需要执行n‐1次递归调用,且第i次划分需要经过n‐i次关键字的比较才能找到第i个记录,也就是枢轴的位置,因此比较次数为
时间复杂度是O(n2);
最优情况下:
Partition每次都划分得很均匀,如果排序n个关键字,其递归树的深度就为log2( 2 n + 1 ),节点个数为n。
分成n块:T(n)≤nT(1)+(log2n)*n=O(nlog2n)
时间复杂度是O(nlogn);
平均情况下:
平均时间复杂度是O(nlogn);
④ 空间复杂度:主要是递归造成的栈空间的使用,最好情况,递归树的深度为log2n,其空间复杂度就为O(logn)
2.合并排序
① 测试数据:
② 实验结果
③ 时间复杂度:
merge()函数的时间复杂度为O(n),代码中有2个长度为n的循环(非嵌套),时间复杂度则为O(n);元素长度为n的归并排序所消耗的时间 T[n]:调用Sort()函数划分两部分,那每一小部分排序好所花时间则为 T[n/2],而最后把这两部分有序的数组合并成一个有序的数组,即merge(a,left,mid,right,temp)所花的时间为 O(n);
迭代公式:
由主定理求出时间复杂度为:T[n] = O( nlogn )
因为不管元素在什么情况下都要做这些步骤,所以花销的时间是不变的,所以该算法的最优时间复杂度和最差时间复杂度及平均时间复杂度都是一样的为:O( nlogn )。合并排序的时间复杂度相对冒泡排序、选择排序、插入排序而言比较好,它们的时间复杂度为O(n2)。
④ 空间复杂度:
迭代公式:
空间复杂度也为O(nlogn)
四、总结
经过此次实验,我对快速排序和归并排序有了更深的了解。从时间复杂度来讲,归并排序的稳定性要比快速排序高,二者时间复杂度相当,快速排序的平均时间复杂度是O(nlogn),最坏情况下是O (n2),最优情况下是O(nlogn);归并排序最优时间复杂度和最差时间复杂度及平均时间复杂度都是一样的为O( nlogn )。快速排序与归并排序,都是将数组分为两个部分,然后两部分再次进行递归,都是运用了分治的思想,只不过快排每次将数组一分为三(把排好序的那个数字摘出来),归并排序每次将数组一分为二。从时间复杂度来讲,归并排序的稳定性要比快速排序高,二者时间复杂度相当。
这次实验让我更加理解分治的思想,对算法知识的运用更加熟练,我深刻明白了算法的重要性,程序的编写离不开它,而且可以培养我们的思维能力,培养我们的思考分析、解决问题的能力。不同的算法都会有不同的解决方法,空间复杂度和时间复杂度都不太相同,合适的算法会更加高效,之后我会继续学习,体验其中的奥妙。
五、参考文献及附录
1.快速排序代码
#include<stdio.h>
#include<iostream>
using namespace std;
int arr[1000];
void quick_sort(int left,int right)
{
if (left < right)
{
int i = left, j = right, k = arr[left];
while (i < j)
{
while (i < j && arr[j] >= k) //从右向左,找小于K的数
j--;
if (i < j)
swap(arr[i++], arr[j]);
while (i < j && arr[i] < k) //从左向右,找大于K的数
i++;
if (i < j)
swap(arr[i], arr[j--]);
}
quick_sort(left, i - 1); //左右递归调用
quick_sort(i + 1, right);
}
}
int main()
{
int n;
cin >> n;
for (int i = 0; i < n; i++) //输入
{
cin >> arr[i];
}
quick_sort(0, n - 1); //快排
for (int i = 0; i < n; i++) //输出
{
cout << arr[i] << " ";
}
cout << endl;
return 0;
}
3.合并排序代码
#include"iostream"
using namespace std;
void merge(int a[],int left,int mid,int right,int temp[])
{
int i = left;
int j = mid + 1; //避免重复比较a[mid]
int t = 0;
while(i <= mid && j <= right) //数组a[left,mid]与数组(mid,right]均没有全部归入数组temp中去
{
//a[i]小于等于a[j],将a[i]的值赋给temp[k],i+1,k+1,后移一位
if(a[i] <= a[j]) temp[t++] = a[i++];
//否则,将a[j]的值赋给temp[k],j,k各加一
else temp[t++] = a[j++];
}
//将数组a[left,mid]剩下的值,逐一归入数组temp
while(i <= mid) temp[t++] = a[i++];
//数组a[left,mid]已经全部归入到temp数组中,而数组(mid,high]还有剩余
while(j <= right) temp[t++] = a[j++];
//将归并后的数组的值逐一赋给数组a[left,right]
for(i = 0; i < t; i++)
a[left+i] = temp[i];
}
void sort(int temp[],int a[],int left,int right)
{
if(left < right)
{
int mid = left + (right - left) / 2;
sort(temp,a,left,mid); //先把左边排序
sort(temp,a,mid + 1,right); //右边排序
merge(a,left,mid,right,temp); //合并到一起
}
}
int main()
{
int num;
cin >> num;
int a[num] = {0};
for(int i = 0; i < num; i++)
{
cin >> a[i];
}
int temp[num] = {0};
sort(temp,a,0,num-1);
int i;
for(i = 0; i < num - 1; i++)
{
if((i + 1) % 10 == 0)
cout << a[i] << endl;
else
cout << a[i] <<" "; //两个空格
}
cout << a[i] << endl;
}