PAT.Basic.Level.1035.插入与归并

浙江大学计算机程序设计能力考试(Programming Ability Test,简称PAT)是由浙江大学计算机科学与技术学院发起并组织的统一考试,旨在展现考生分析问题、解决问题和计算机程序设计的能力,科学评价计算机程序设计人才,并为企业选拔人才提供参考标准。

本文分享博主在PAT网站练习时提交的代码,供大家讨论,指出弊病,共同提高!

#########################

#题库:PAT (Basic Level) Practice (中文)

#题号:1035

#题目:插入与归并

#实现语言: C

------------------------------------ 正文 ------------------------------------

根据维基百科的定义:

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

归并排序进行如下迭代操作:首先将原始序列看成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
------------------------------------ 正文结束 ------------------------------------


【浅析】

题目保证结果唯一,则判定排序方式的思路为:

1、先找到第一个逆序对,且此逆序对一定存在。

2、观察第一个逆序对之后的所有元素,是否与原数列相应位置元素相同。是则为插入排序,否则为归并排序。

得知了排序方式后需要输出下一个迭代的中间序列,插入排序极其简单,不多做讨论。

对于归并排序,这里使用迭代方式实现。先判断当前迭代规模(1、2、4、8、16、、、等)然后用下一个规模继续迭代。


【代码】

#include <stdio.h>
#include <stdlib.h>
//归并函数,将三个界桩中间的两列元素归并。数组temp是缓冲区域,应该与s的尺寸相同。
void Merge(int *s, int lo, int mi, int hi, int *temp){
	if (mi - lo < 1 && hi - mi < 1)
		return;
	int i, j, k;
	//Copy to temp
	for (int i = lo; i < mi; i++)
		temp[i] = s[i];
	//Merge
	j = mi; k = lo;
	for (i = lo; i < hi; i++)
	{
		if (temp[k] < s[j])
			s[i]=temp[k++];
		else s[i] = s[j++];
		if (k == mi) break;
		if (j == hi) {
			for (i++; i < hi; i++, k++){
				s[i] = temp[k];
			}	
			break;
		}
	}
	return;
}

int main(int argc, char const *argv[])
{
	int N, *arr, *tar;
	int fir;
	int isMerge = 0;
	scanf("%d", &N);
	arr = malloc(N * sizeof(int));
	tar = malloc(N * sizeof(int));

	for (int i = 0; i < N; i++)
		scanf("%d", arr + i);
	for (int i = 0; i < N; i++)
		scanf("%d", tar + i);

	//寻找第一个逆序对,并将其首元素位置记在fir处
	fir = 0;
	while(tar[fir] <= tar[fir + 1]){
		fir++;
	}
	//比较第一个逆序对后的所有元素,与原数列相应位置元素关系,
	//据此判别排序方式
	for (int i = fir + 1; i < N; i++)
		if (tar[i] != arr[i]) {
			isMerge = 1;
			break;
		}

	if(isMerge){
		//是归并排序,接下来先分析迭代规模,再进行下一次迭代
		printf("Merge Sort\n");
		int i, j, found;
		found = 0;

		//分析迭代规模的代码较为复杂
		//思路并不难理解,分别按相应的迭代规模i进行分组,在每一组内查找有无逆序对
		//   如果发现逆序对,则当前迭代规模就是目前的i
		//   如果都没有,则迭代规模翻倍
		for (i = 2; i < N; i=i<<1)
		{
			for (j = 0; j < N/i; j++)
			{
				for (int k = i * j; k < i * ( j + 1 ) - 1; k++)
					if (tar[k] > tar[k + 1]){found = 1; break;}
				if(found) break;
			}
			if(found) break;
			for (int k = i * (j + 1); k < N - 1; k++)
				if (tar[k] > tar[k + 1]){found = 1; break;}
			if(found) break;
		}
		//用迭代规模i进行下一次归并迭代
		for (int k = 0; k < N / i; k++)
			Merge(tar, i * k, i * k + (i>>1), i * k + i, arr);
		if (N%i > (i>>1)) Merge(tar, N - N%i, N - N%i + (i>>1), N, arr);

	}else{
		//是插入排序,下一次迭代只需要将[fir + 1]元素插入到合适位置即可
		printf("Insertion Sort\n");
		int buf;
		while(tar[fir] > tar[fir + 1]){
			buf = tar[fir];
			tar[fir] = tar[fir + 1];
			tar[fir + 1] = buf;
			fir--;
		}
	}

	//第二次迭代完成,在此输出结果序列
	for (int i = 0; i < N - 1; i++)
		printf("%d ", tar[i]);
	printf("%d\n", tar[N - 1]);

	free(arr);
	free(tar);
	return 0;
}

以上代码已经过测试。


本文的分析和代码均系原创,如果对你有帮助,或者你有意见看法,欢迎留言,博主将倍感荣幸!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值