浙江大学计算机程序设计能力考试(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;
}
以上代码已经过测试。
本文的分析和代码均系原创,如果对你有帮助,或者你有意见看法,欢迎留言,博主将倍感荣幸!