PTA 09-排序2 Insert or Merge(C语言,拆分详解哦~)

09-排序2 Insert or Merge

分数 25

作者 陈越

单位 浙江大学

According to Wikipedia:

Insertion sort iterates, consuming one input element each repetition, and growing a sorted output list. Each iteration, insertion sort removes one element from the input data, finds the location it belongs within the sorted list, and inserts it there. It repeats until no input elements remain.

Merge sort works as follows: Divide the unsorted list into N sublists, each containing 1 element (a list of 1 element is considered sorted). Then repeatedly merge two adjacent sublists to produce new sorted sublists until there is only 1 sublist remaining.

Now given the initial sequence of integers, together with a sequence which is a result of several iterations of some sorting method, can you tell which sorting method we are using?

Input Specification:

Each input file contains one test case. For each case, the first line gives a positive integer N (≤100). Then in the next line, N integers are given as the initial sequence. The last line contains the partially sorted sequence of the N numbers. It is assumed that the target sequence is always ascending. All the numbers in a line are separated by a space.

Output Specification:

For each test case, print in the first line either "Insertion Sort" or "Merge Sort" to indicate the method used to obtain the partial result. Then run this method for one more iteration and output in the second line the resuling sequence. It is guaranteed that the answer is unique for each test case. All the numbers in a line must be separated by a space, and there must be no extra space at the end of the line.

Sample Input 1:

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

Sample Output 1:

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

Sample Input 2:

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

Sample Output 2:

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

大意

        该题给出一个原序列,再给出一个未排序完成的序列,让你通过此序列判断使用的是什么排序方法并给出下一次排序得到的序列。

步骤:

1. 首先我们要知道如果不是一种排序方法就会是另一种,所以,我们先解决一种,因为处插入排序比较简单,所以先吃掉它~

void Insertion_Sort(int A[],int N){
	//插入排序 
	int i,p,tmp;
	for(p=1;p<N;p++){//p从一开始 
		tmp=A[p];
		for(i=p;i>0&&A[i-1]>tmp;i--)
			A[i]=A[i-1];
		A[i]=tmp;
	}
}

当然啦,我们得对它动点手脚才行哈哈哈哈~

在第一个for循环里面,每次都把一个数放在它应该在的地方,所以每次都要和给定排到一半的序列(俺放在了B[ ])比较一下,看看相不相同 。

int Judge(int A[],int B[]){
	int flag=1;
	for(int i=0;i<N;i++){
		if(A[i]!=B[i]){
			flag=0;
			break;
		}
	}
	return flag;
}

如果相同的话,立即输出“Insertion Sort”,并输出下一次排序得到的序列。

int Insertion_Sort(int A[],int B[],int N){
	//插入排序 
	int i,p,tmp,k,flag=0;
	for(p=1;p<N;p++){
		tmp=A[p];
		for(i=p;i>0&&A[i-1]>tmp;i--)
			A[i]=A[i-1];
		A[i]=tmp;
		if(Judge(A,B)){
			printf("Insertion Sort\n");
			k=p+1;//记录下一次排序中的p值 
			flag=1;
		}
		if(p==k){
			for(i=0;i<N;i++){
				if(i==0)printf("%d",A[i]);
				else printf(" %d",A[i]);
			}
			break;
		}	
	}
	return flag;
}

 2. 然后我们来解决归并排序!这里有一个易错点需要注意!在测试用例2,我们可以明显看到这里的归并排序不是递归版本的,而是循环版本的呜呜(俺在这里挣扎了很久,后面才知道的呜),所以在以后做题时要把题目读懂并且!没有太复杂的话,测试用例自己过一遍!!!(如果不会归并算法的非递归写法,可以滑到该文末,有详细的步骤哦~这里先给出呢)

void Merge(int A1[],int tmp[],int L,int R,int Rend){
	//tmp数组为临时数组,L为第一个序列的起始下标,R是第二个序列的起始下标,Rend是是第二个序列最后一个元素的下标 
	int k=L,Lend=R-1;
	int length=Rend-L+1;
	while(L<=Lend&&R<=Rend){
		if(A1[L]<=A1[R])tmp[k++]=A1[L++];
		else tmp[k++]=A1[R++];
	}
	while(L<=Lend)tmp[k++]=A1[L++];
	while(R<=Rend)tmp[k++]=A1[R++];
	for(int i=Rend;i>Rend-length;i--){
		A1[i]=tmp[i];
	}
}
void Merge_Pass(int A[],int tmp[],int B[],int len,int N){
	int i;
	for(i=0;i<=N-2*len;i+=2*len){
		Merge(A,tmp,i,i+len,i+2*len-1);
	}//这里 i<=N-2*len是如何得到的呢?是因为 i+2*len-1必须小于N,所以 i<=N-2*len
	if(i+len<N){//如果只剩一个 完整序列 
		Merge(A,tmp,i,i+len,N-1);
	}//那如果一个完整序列都不剩,其实不用管,反正都在A数组里面的 
	if(Judge(tmp,B,N)){
		printf("Merge Sort\n");
		F=len;//方便输出下一次排好序之后的序列 
	}
	if(len==2*F){
		for(i=0;i<N;i++){
			if(i==0)printf("%d",tmp[i]);
			else printf(" %d",tmp[i]);
		}
	}
}
void Merge_Sort(int A[],int B[],int N){
	//归并排序的时间复杂度是O(NlogN),但是需要额外的O(N)的空间复杂度
	//所以一般不用于内部排序,多用于外部排序 
	int *tmp,length=1;
	tmp=(int *)malloc(N*sizeof(int));//临时数组 
	while(length<N){
		Merge_Pass(A,tmp,B,length,N);
		length*=2;
	}
	free(tmp);//记得free掉哦 
} 

完整代码: 

#include<stdio.h>
#include<stdlib.h>
#define MaxSize 105
int F;
int Judge(int A[],int B[],int N){
	int flag=1;
	for(int i=0;i<N;i++){
		if(A[i]!=B[i]){
			flag=0;
			break;
		}
	}
	return flag;
}
int Insertion_Sort(int A[],int B[],int N){
	//插入排序 
	int i,p,tmp,k,flag=0;
	for(p=1;p<N;p++){
		tmp=A[p];
		for(i=p;i>0&&A[i-1]>tmp;i--)
			A[i]=A[i-1];
		A[i]=tmp;
		if(Judge(A,B,N)){
			printf("Insertion Sort\n");
			k=p+1;//记录下一次排序中的p值 
			flag=1;
		}
		if(p==k){
			for(i=0;i<N;i++){
				if(i==0)printf("%d",A[i]);
				else printf(" %d",A[i]);
			}
			break;
		}	
	}
	return flag;
}
void Merge(int A1[],int tmp[],int L,int R,int Rend){
	//tmp数组为临时数组,L为第一个序列的起始下标,R是第二个序列的起始下标,Rend是是第二个序列最后一个元素的下标 
	int k=L,Lend=R-1;
	int length=Rend-L+1;
	while(L<=Lend&&R<=Rend){
		if(A1[L]<=A1[R])tmp[k++]=A1[L++];
		else tmp[k++]=A1[R++];
	}
	while(L<=Lend)tmp[k++]=A1[L++];
	while(R<=Rend)tmp[k++]=A1[R++];
	for(int i=Rend;i>Rend-length;i--){
		A1[i]=tmp[i];
	}
}
void Merge_Pass(int A[],int tmp[],int B[],int len,int N){
	int i;
	for(i=0;i<=N-2*len;i+=2*len){
		Merge(A,tmp,i,i+len,i+2*len-1);
	}//这里 i<=N-2*len是如何得到的呢?是因为 i+2*len-1必须小于N,所以 i<=N-2*len
	if(i+len<N){//如果只剩一个 完整序列 
		Merge(A,tmp,i,i+len,N-1);
	}//那如果一个完整序列都不剩,其实不用管,反正都在A数组里面的 
	if(Judge(tmp,B,N)){
		printf("Merge Sort\n");
		F=len;//方便输出下一次排好序之后的序列 
	}
	if(len==2*F){
		for(i=0;i<N;i++){
			if(i==0)printf("%d",tmp[i]);
			else printf(" %d",tmp[i]);
		}
	}
}
void Merge_Sort(int A[],int B[],int N){
	//归并排序的时间复杂度是O(NlogN),但是需要额外的O(N)的空间复杂度
	//所以一般不用于内部排序,多用于外部排序 
	int *tmp,length=1;
	tmp=(int *)malloc(N*sizeof(int));//临时数组 
	while(length<N){
		Merge_Pass(A,tmp,B,length,N);
		length*=2;
	}
	free(tmp);//记得free掉哦 
} 
int main(){
	int i,N;
	scanf("%d",&N);
	int A[N],A1[N],B[N];
	for(i=0;i<N;i++){
		scanf("%d",A+i);
		A1[i]=A[i];
	}
	for(i=0;i<N;i++){
		scanf("%d",B+i);
	}
	if(!Insertion_Sort(A,B,N))//因为此步骤已经破坏了A数组,所以多开了一个数组A1 
		Merge_Sort(A1,B,N);
	return 0;
}

提交结果:

 

归并算法的非递归算法来啦~ 

1. 首先我们要知道该算法的主要思想就是将待排序列拆分为多个有序序列,然后两两合并并排至有序,所以我们先写出两个序列合并为一个序列的算法Merge。

void Merge(int A1[],int tmp[],int L,int R,int Rend){
	//tmp数组为临时数组,L为第一个序列的起始下标,R是第二个序列的起始下标,Rend是是第二个序列最后一个元素的下标 
	int k=L,Lend=R-1;
	int length=Rend-L+1;
	while(L<=Lend&&R<=Rend){
		if(A1[L]<=A1[R])tmp[k++]=A1[L++];
		else tmp[k++]=A1[R++];
	}
//处理尾巴
	while(L<=Lend)tmp[k++]=A1[L++];
	while(R<=Rend)tmp[k++]=A1[R++];
	for(int i=Rend;i>Rend-length;i--){
		A1[i]=tmp[i];//将数据导回A1数组里面去
	}
}

2. 然后在1之上,得将序列全部数据都以len为有序序列的长度进行两两合并 。记得每次完成之后都要与数组B进行比较哦~

void Merge_Pass(int A[],int tmp[],int B[],int len,int N){
	int i;
	for(i=0;i<=N-2*len;i+=2*len){
		Merge(A,tmp,i,i+len,i+2*len-1);
	}//这里 i<=N-2*len是如何得到的呢?是因为 i+2*len-1必须小于N,所以 i<=N-2*len
	if(i+len<N){//如果只剩一个 完整序列 
		Merge(A,tmp,i,i+len,N-1);
	}//那如果一个完整序列都不剩,其实不用管,反正都在A数组里面的 
	if(Judge(tmp,B,N)){
		printf("Merge Sort\n");
		F=len;//方便输出下一次排好序之后的序列 
	}
	if(len==2*F){
		for(i=0;i<N;i++){
			if(i==0)printf("%d",tmp[i]);
			else printf(" %d",tmp[i]);
		}
	}
}

3. 最后就是在2之上,进行len的变化。

void Merge_Sort(int A[],int B[],int N){
	//归并排序的时间复杂度是O(NlogN),但是需要额外的O(N)的空间复杂度
	//所以一般不用于内部排序,多用于外部排序 
	int *tmp,length=1;
	tmp=(int *)malloc(N*sizeof(int));//临时数组 
	while(length<N){
		Merge_Pass(A,tmp,B,length,N);
		length*=2;
	}
	free(tmp);//记得free掉哦 
} 

大功完成!希望对你有所帮助鸭~有错误的话,欢迎指出嘻嘻~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值