题目分析:
这道题目,花了不少时间,犯了一个很窒息也让我查了很久很才发现的错误——把Insertion Sort输出成了Insert Sort。。。写这道题的分析时,身心俱疲。中途因为查不出错,借鉴了网上的一些代码并相应地修改了,所以和网上很多其他人代码比较相似。
这道题目从0~6一共7个测点,0、2、4是插入排序测点,其余是归并排序测点(看看我的错误想必就能明白为何我知道这些测点的情况了。。)
把思路弄清楚其实写起来倒也不太麻烦,首先关于判定是何种排序,可以先找出第一个未排好序数据的位置,若第一个未排好序及之后的每一个数都与原序列对应相等,那么一定是插入排序,否则就是归并排序。当然这个判断其实仅适合这道题目,很容易看出来这个判断有很大的漏洞,不过题目说了给出的序列不会有歧义,所以放心大胆用就行。
另外,在这里需要说明和强调一下,网上不少相关的博客其实说的不对,肯定没经过自己认真思考,题目这里的归并排序并不是递归版,递归版的归并排序会把序待排序列分成两半,先将左半部分全部排完,才会开始对右半部分进行排序,也就是说前面几步排序后,序列后半部分很可能是不会有变化的。
明确了这个要求后,编写再排序一步的算法也就比较容易了。归并排序进一步排序的算法,在修改代码时以为是自己真的写错了就按人家的改了。。。后来发现原因竟然是printf的问题。。心太累就不想再重新写一遍改回原来的了。在后面简单介绍一下我之前的算法思路。
现在的代码和网上其他类似,但有修改。在这里一遍遍地排序时,越到后面数组越来越有序,而在数组基本有序的情况下,插入排序就的性能就会高很多,本身已经有序时的复杂度是O(n)O(n)O(n),所以在这里我写了简单的插排。(不过题目数组长度小,最多就100,随你怎么折腾,写个冒泡排序都不会有问题)。
为了进一步发挥插排的性能,我没有向网上很多人那样从原始数组出发排序直到与部分排序数组相等,而是选择从部分排好序的数组出发,逐步进行区间排序,直到某次排序后数组发生了变化,即说明新进行了一轮归并排序,可以停止并输出了。
我之前采用的方式是按照归并排序的思路,找出最短的未排好序的区间长度d,然后再以此区间长度排序一遍即可,判断数组未排序好的区间长度的代码实际上不会出现重复判断,所以最多对n个元素各自比较一次,这个函数复杂度是O(n)O(n)O(n),而给出长度后再排一步也很容易实现复杂度为O(n)O(n)O(n)的代码,从渐进意义上来说我之前的算法比我现在按照网上其他代码修改的算法好,但是写起来稍微复杂点,而题目在复杂度上基本没啥要求,在PAT这种以写的快能过为目的的条件下,这么干意义就不是很大了。
源代码
#include <stdio.h>
void insertSort(int*arr,int sta,int end);
int main()
{
int num,posit;
scanf("%d",&num);
int raw[100],partSort[100]; ///存储原始序里和部分排好序的序列
for(int i=0;i<num;++i)
scanf("%d",&raw[i]);
for(int i=0;i<num;++i)
scanf("%d",&partSort[i]);
for(posit=1;posit<num&&partSort[posit]>=partSort[posit-1];++posit); //找出无序的第一个位置
int j;
for(j=posit;j<num&&partSort[j]==raw[j];++j)
;
if(j==num){ //若剩下数与原始数组相同,则一定为插入排序
printf("Insertion Sort\n");
insertSort(partSort,0,posit);
}
else{ //否则flag为0,为归并排序
printf("Merge Sort\n");
int i,gap=2,flag=1,limit;
int tmp[100];
for(i=0;i<num;++i) tmp[i]=partSort[i];
while(flag){
limit=num/gap;
for(i=0;i<limit;++i)
insertSort(partSort,i*gap,(i+1)*gap-1);
insertSort(partSort,i*gap,num-1);
gap<<=1;
for(i=0;i<num;++i) //对部分排序数组进行进一步排序后已经得到了新结果,可以停止循环
if(tmp[i]!=partSort[i]){ flag=0; break;}
}
}
for(j=0;j<num-1;++j)
printf("%d ",partSort[j]);
printf("%d",partSort[num-1]);
return 0;
}
void insertSort(int *arr,int begin,int end)
{
int tmp,j;
for(int i=begin+1;i<=end;++i){
j=i;
tmp=arr[j];
while(j>begin&&tmp<arr[j-1]){
arr[j]=arr[j-1];
j--;
}
arr[j]=tmp;
}
}
本文详细分析PAT基础级别题1035,探讨如何判断插入排序与归并排序,并分享错误经验。文章指出,通过查找未排序数据位置来区分排序类型,然后利用特定的排序策略完成每一步操作。虽然现有代码基于网上的解决方案进行了调整,但作者指出原有的算法在复杂度上有优势,但在PAT考试中追求快速解题,简化代码更为重要。

271

被折叠的 条评论
为什么被折叠?



