PAT乙级|C语言|1035 插入与归并 (25分)

题目

根据维基百科的定义:

插入排序是迭代算法,逐一获得输入数据,逐步产生有序的输出序列。每步迭代中,算法从输入序列中取出一元素,将之插入有序序列中正确的位置。如此迭代直到全部元素有序。

归并排序进行如下迭代操作:首先将原始序列看成 N 个只包含 1 个元素的有序子序列,然后每次迭代归并两个相邻的有序子序列,直到最后只剩下 1 个有序的序列。

现给定原始序列和由某排序算法产生的中间序列,请你判断该算法究竟是哪种排序算法?

输入格式:
输入在第一行给出正整数 N (≤100);随后一行给出原始序列的 N 个整数;最后一行给出由某排序算法产生的中间序列。这里假设排序的目标序列是升序。数字间以空格分隔。

输出格式:
首先在第 1 行中输出Insertion Sort表示插入排序、或Merge Sort表示归并排序;然后在第 2 行中输出用该排序算法再迭代一轮的结果序列。题目保证每组测试的结果是唯一的。数字间以空格分隔,且行首尾不得有多余空格。

输入样例 1:

10
3 1 2 8 7 5 9 4 6 0
1 2 3 7 8 5 9 4 6 0

输出样例 1:

Insertion Sort
1 2 3 5 7 8 9 4 6 0

输入样例 2:

10
3 1 2 8 7 5 9 4 0 6
1 3 2 8 5 7 4 9 0 6

输出样例 2:

Merge Sort
1 2 3 8 4 5 7 9 0 6

AC代码

  • 区分出中间序列是哪种排序算法产生的,其实只需判断“插入排序能否产生该中间序列”即可。如果插入排序可以产生,那么归并排序必然不能有该中间序列,因为如果归并排序也能产生该中间序列,那么两种排序都是可能的,无法判断准确结果,而题目保证不会有这种情况。既然题目保证了两种排序非此即彼,那么只判断一个更容易判断的插入排序即可。
  • 从后往前逐一对比原始序列与中间序列,定位最后一个发生改变的数组元素。检测该元素之前的所有元素是否有序,若有序,则为插入排序;若不为有序,则为归并排序。
  • 按中间序列的排序算法,用该算法迭代一次输出:根据插入排序的特点,若已排序前n个元素,迭代一次即将前n+1个元素排序即可;而归并排序,观察样例,是由循环实现的归并,需要处理小尾巴(每次迭代之后,每 2 n 2^n 2n个元素为有序,而末尾不足 2 n 2^n 2n个元素的元素集合是要特殊处理的小尾巴)。迭代一次不需要自己写相应的排序算法,按特点将特定元素用库函数qsort排序即可。
  • 测试点2:对于例如 原始序列:1 9 5 4 10 11 8 中间序列:1 4 5 9 10 11 8 这类迭代次数不确定的插入排序(可以是迭代4次也可以是迭代5次、6次,第5,6次因为10,11插在原位置上,与不迭代的结果是一样的),题目是要求输出下一个与中间序列不同的结果。即输出:1 4 5 8 9 10 11
#include<stdio.h>
#include<stdlib.h>
int N,num1[100],num2[100];
int cmp(void *a,void *b){	//qsort回调函数
    return *(int *)(a)-*(int *)(b);
}
int judge(){	//判断排序算法
    int flag=0;
    for(int i=N-1;i>=0;i--){	//定位最后一个发生改变的元素的位置
        if(num1[i]!=num2[i]){
            flag=i;
            break;
        }
    }
    for(int i=0;i<flag;i++){	//检查该元素之前的元素是否有序
        if(num2[i]>num2[i+1]){	
            printf("Merge Sort\n");	//非有序则输出归并排序并返回1
            return 1;
        }
    }
    printf("Insertion Sort\n");		//有序则输出插入排序,顺便迭代一次
    for(int i=flag;i<N;i++){	//因为测试点2的问题需要找到下一个逆序的元素,将其插入
    	if(num2[i]>num2[i+1]){
    		flag=i;
    		break;
		}
	}
    qsort(num2,flag+2,sizeof(int),cmp);
    return 0;
}
int main()
{
    scanf("%d",&N);
    for(int i=0;i<N;i++)
        scanf("%d",&num1[i]);
    for(int i=0;i<N;i++)
        scanf("%d",&num2[i]);
    if(judge()==1){	//若为归并排序,则迭代一次
        for(int i=2;i<=N;i=i*2){
            int lim=N/i*i,flag=0;	//lim为去掉小尾巴的元素个数
            for(int j=0;j<lim;j=j+i){		//在每2的i次方个元素中寻找逆序元素,标记
                for(int k=j;k<j+i-1;k++){
                    if(num2[k]>num2[k+1]){	
                        flag=1;
                    }
                }
            }
            if(flag){	//被标记有逆序元素,将每2的i次方个元素进行排序作为一次迭代,并退出循环
                for(int j=0;j<lim;j=j+i)
                    qsort(num2+j,i,sizeof(int),cmp);
                if(lim<N)qsort(num2+lim,N-lim,sizeof(int),cmp);	//若有小尾巴处理小尾巴
                break;
            }
        }
    }
    printf("%d",num2[0]);	//输出
    for(int i=1;i<N;i++){
        printf(" %d",num2[i]);
    }
    return 0;
}
  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值