浙大《数据结构》09-排序2 Insert or Merge

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 ) N(≤100) N(100). Then in the next line, N N N integers are given as the initial sequence. The last line contains the partially sorted sequence of the N N 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
思路

此题因为题目限定只有可能是插入或者归并,所以只要能判断其中一种,那么剩下的就是另外一种。插入排序比较好判断,特点就是序列的前面一部分已经排好序(即非逆序),从某一个元素开始出现逆序,之后的元素和原数组元素完全相同。
所以只需要用两个while循环,第一个检查相邻元素是否逆序,发现逆序后退出,再进入另一个while循环检查后面的元素是否和原数组完全一样。
至于在判断排序类型后再进行一次迭代。对于插入排序,记录下逆序发生的那个元素i,就可以知道下一次迭代要插入的元素,直接插入即可。
对于归并排序。我开始以为上面的i就说明现在以i为长度的子序列已经排好序,下一步就是对长度为i的子序列两两进行归并。这是有漏洞的,比如对于下面的序列

1 3 5 6 2 7 4 8

假设该序列已经完成长度为2的子序列的排序,下一步应该每4个进行归并排序,但是按照上面的循环,i应该是4,也就是完成了长度为4的子序列的排序,下一步应该每8个进行归并。但显然2 7 4 8并未排好序。所以判断当前已经排好序的子列长度,应该从长度为2开始,检查每一个该长度的子列是否排好序。贴一下该部分的代码:
(此部分参考博客)

int length = 2;
int flag = 1;
for (; length <= N && flag; length *= 2) {
	//在长度为length片段满足顺序排列的基础上判断length*2
	for (int i = length; i < N; i += length * 2) {
		if (partial_A[i - 1] > partial_A[i]) { 
		//检查长度为length*2的子序列的中间是否逆序,因为前提是长度为length序列已排好序
			flag = 0;
			break; //说明2*length未排好
		}
	}
}
// 上面break退出时,因外层for循环的缘故,length会先*2退出

另外我开始写归并的时候传入参数只有数组、起始索引和中止索引,以为center索引直接取中值就好。但是归并排序序列长度并不一定是偶数,针对奇数长度的序列,归并时必须准确给出要合并的两个子序列的分割点,原因是归并的前提是子序列有序,而分割点不准确就不能保证这一点。
完整代码:

#include
using namespace std;

void printA(int A[], int N) {
	for (int i = 0; i < N; i++) {
		printf("%d", A[i]);
		if (i != N - 1)printf(" ");
		else printf("\n");
	}
}

void merge(int A[], int p, int q, int R_start) { //归并[p,q]之间的序列
	int length = q - p + 1; 
	int *tempArray = (int*)malloc(length * sizeof(int));

	int ptr1 = p;
	int ptr2 = R_start;
	int ptr = 0;

	while (ptr1 <= (R_start - 1) && ptr2 <= q) {
		if (A[ptr1] < A[ptr2]) tempArray[ptr++] = A[ptr1++];
		else tempArray[ptr++] = A[ptr2++];
	}

	while (ptr1 <= (R_start - 1))
		tempArray[ptr++] = A[ptr1++];
	while (ptr2 <= q)
		tempArray[ptr++] = A[ptr2++];
		
	for (int i = p; i <= q; i++)
		A[i] = tempArray[i - p];
}

int main() {
	int N = 0;
	cin >> N;
	int *A = (int *)malloc(N * sizeof(int));
	for (int i = 0; i < N; i++)
		cin >> A[i];
	int *partial_A = (int *)malloc(N * sizeof(int));
	for (int i = 0; i < N; i++)
		cin >> partial_A[i];
	//判断是否是插入排序,否则为归并排序
	int j = 0;
	while (partial_A[j] <= partial_A[j + 1])
		j++;
	int index = j + 1; //记录
	while (partial_A[++j] == A[j])
		if (j == N - 1)break;
	if (j == N - 1) {//即从第一个逆序发生后,后面和A一样
		printf("Insertion Sort\n");
		int temp = partial_A[index];
		int i = 0;
		for (i = index; i > 0 && partial_A[i - 1] > temp; i--)
			partial_A[i] = partial_A[i - 1];
		partial_A[i] = temp;
	}
	else {
		printf("Merge Sort\n");
		int length = 2;
		int flag = 1;
		for (; length <= N && flag; length *= 2) {
			for (int i = length; i < N; i += length * 2)
				if (partial_A[i - 1] > partial_A[i]) { //第length个元素和它右边比较
					flag = 0;
					break; //说明2*length未排好
				}
		}
		// 上面break退出时,因外层for循环的缘故,length会*2退出
		int p = 0, q = length - 1;
		while (q < N) {
			//归并[p,q]之间的序列
			merge(partial_A, p, q, p + length / 2);
			//更新区间
			p = q + 1;
			q = p + length - 1;
		}
		if (p + length / 2 - 1 < N)//最后剩两个子列
			merge(partial_A, p, N - 1, p + length / 2);
	}
	printA(partial_A, N);
}

你可能感兴趣的:(浙大《数据结构》编程作业,数据结构,排序算法,插入排序)