PAT_乙级 1035 插入与归并

1.题目描述:

根据维基百科的定义:
插入排序是迭代算法,逐一获得输入数据,逐步产生有序的输出序列。每步迭代中,算法从输入序列中取出一元素,将之插入有序序列中正确的位置。如此迭代直到全部元素有序。
归并排序进行如下迭代操作:首先将原始序列看成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

2.解题思路:

根据题目描述,结果只可能是插入排序或归并排序。可以根据插入排序的特点(即插入排序前几个已经排序完成的数都是有序的,后面的数都是和原数组相同的)判断是否为插入排序,否则即为归并排序。主要的坑在于归并排序过程中,虽然还没有进行排序,但是有几个数已经凑巧有序了,这样会影响归并排序进行到哪一步的判断。如,有一个数组为{2,1,3,4,7,9,6,8,5},经过一次排序之后为{1,2,3,4,7,9,6,8,5}。有可能会以为已经对长度为4的子数组进行了排序,而实际上却刚好将长度为2的子数组进行了排序。为了避免这种错误。我们要取有序子数组中长度最小的且为2的倍数的作为下一步排序的单位子数组。

3.代码部分:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

void Msort(int *b,int *count,int max);
void Isort(int *b,int *count);
int judge(int *a,int *b,int count,int max);
int cmp(const void *a,const void *b);

int main(void)
{
	int max=0,i=0,j=0,flag=1;
	int a[200]={0},b[201]={0};
	int count[201]={1};
	scanf("%d",&max);
	for(i=0;i<max;i++)
		scanf("%d",&a[i]);
	scanf("%d",&b[0]); 	//排序后的数组 
	for(i=1;i<max;i++)
	{
		scanf("%d",&b[i]);
		if(b[i]>=b[i-1])			//将顺序排列的个数记录下来,如果不顺序排列则记录在下一个组 
			count[j]++;
		if(b[i]<b[i-1])
			count[++j]=1;
	}
	if(judge(a,b,count[0],max)==0)
		flag=0;
	if(flag==1)
	{
		printf("Insertion Sort\n");
		Isort(b,count);
	}
	if(flag==0)
	{
		printf("Merge Sort\n");
		Msort(b,count,max);
	}
	printf("%d",b[0]);
	for(i=1;i<max;i++)
		printf(" %d",b[i]);	
	return 0;
}
int cmp(const void *a,const void *b)
{
	return *(int *)a-*(int *)b;
}

void Msort(int *b,int *count,int max)
{
	int i=0,min=count[0],total=0,j=0;
	for(i=0;count[i+1]!=0;i++)
	{
		if(min>count[i])	//这个复杂的求最小子数组长度是为了防止出现如子数组长度为6,8的情况 
		{					//明显6不可能成为子数组长度。只有2的n次方才有可能。 
			for(j=2;count[i]%j==0&&min%j==0;j*=2);
			min=j/2;
		}
	}
	while(total+min*2<=max)	//前面的部分再进行一次两个数组合并排序 
	{
		qsort(b+total,min*2,sizeof(int),cmp); 
		total+=min*2;
	}
	if(max-total>min)	//最后一个数组有可能不为2的倍数,但如果需要,也要和它前面的子数组合并 
		qsort(b+total,min+max%min,sizeof(int),cmp);
}

void Isort(int *b,int *count)	//插入排序只需要把已排序完成的后面一位进行一遍插入排序就可以了 
{
	int i=0,temp=b[count[0]];
	for(i=count[0]-1;i>=0;i--)
	{
		if(temp<b[i])
			b[i+1]=b[i];
		else	break;
	}
	b[i+1]=temp;
}

int judge(int *b,int *a,int count,int max)
{
	int i=0;
	for(i=count;i<max;i++)	//从第一个有序的子数组之后开始对比 
		if(a[i]!=b[i])		//若和原数组对不上,是归并排序。全对上了则是插入排序 
			return 0;
	return 1;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值