算法 排序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). 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.

翻译:

插入排序迭代,每次重复使用一个输入元素,并增加排序的输出列表。每次迭代,插入排序从输入数据中删除一个元素,在排序列表中找到它所属的位置,并将其插入其中。它会重复,直到没有输入元素保留为止。

合并排序的工作原理如下:将未排序的列表分成N个子列表,每个子列表包含1个元素(1个元素的列表被视为已排序)。然后重复合并两个相邻的子列表以生成新的已排序子列表,直到只剩下一个子列表。

现在给定整数的初始序列再加上一个序列,这个序列是某个排序方法多次迭代的结果,你能说出我们使用的是哪种排序方法吗?

输入规格:

每个输入文件包含一个测试用例。对于每种情况,第一行给出一个正整数N(≤100)。在下一行中,N个整数作为初始序列。最后一行包含N个数字的部分排序序列。假设目标序列总是递增的。一行中的所有数字都用空格隔开。

输出规格:

对于每个测试用例,在第一行打印“插入排序”或“合并排序”以指示用于获取部分结果的方法。然后再运行这个方法进行一次迭代,并在第二行输出结果序列。保证每个测试用例的答案都是唯一的。一行中的所有数字都必须用空格隔开,并且行的末尾不能有多余的空格。

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

解答:

难点在于怎么找到它是用的哪种排序。

首先我们知道,如果第二行的末尾顺序是乱的且和第一行末尾相同,则应该是插入排序。因为插入排序是从前到后的,所以只进行有限次迭代的时候不会改变后面还没有迭代到的顺序。而且插入排序的第二行的前面部分一定是已经排好序的。

即插入排序算法迭代一定次数后的序列为:已经排好序的部分+和原序列一样顺序的后半部分。

再考虑一下归并排序,归并排序至少在每一个小单元里是有序的,比如第一次迭代以后,每两个一定是从小到大的,第二次迭代以后,每四个一定是从小到大的,但是我们一定要找出最大的小单元吗?

我们先通过上面的描述,来判断其是否为插入排序。

int ifInsertSort(ElementType ASource[], ElementType ASort[], int N) {
	int i = N - 1;
	for (;i >= 0;i--) {
		if (ASort[i] != ASource[i])break;
	}
	int j = 0;
	for (;j < N-1;j++) {
		if (ASort[j] > ASort[j + 1]) {
			break;
		}
	}
	if (j > i) {
		return 1;//是插入排序
	}
	else {
		return 0;//不是插入排序
	}
}

但是考虑到输出是再迭代一次的结果,所以,这种方法是不行的。因为我们没法从该方法中判断再迭代一次是什么样子。

现在只能用笨办法了:分别去用两种方法迭代,然后每次迭代都判断一次是否和输出一样。

我们首先建立的是三个数组:

ElementType myArray[MaxVertexNum];//存原数据,插入排序用
ElementType myArray_1[MaxVertexNum];//存原数据,归并排序用
ElementType myArray2[MaxVertexNum];//存第二行数据

对于插入排序,我们这么判断:

void InsertionSort(ElementType A[], int N)
{ 
	// 插入排序 
	int P, i;
	ElementType Tmp;
	int 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 (flag == 1)break;
		if (isSame(A, myArray2, N)) {
			flag = 1;
			isInsertFlag = 1;
			cout << "Insertion Sort"<<endl;
		}
	}
}

在执行完以后,判断是不是当前排序的序列和我们读入的第二行的序列的一样,如果一样,flag置1。当判断flag=1的时候,已经又迭代了一遍了,所以break。

同时我们还有一个flag,为 isInsertFlag ,判断是不是插入排序,如果一直都不一样,则说明不是插入排序

	InsertionSort(myArray, N);//插入排序
	if (isInsertFlag) {	
		for (int i = 0;i < N - 1;i++)
			cout << myArray[i] << " ";
		cout << myArray[N - 1];
	}	
	else if (!isInsertFlag) {
		cout << "Merge Sort" << endl;
		Merge_Sort(myArray_1, N);
		for (int i = 0;i < N - 1;i++)
			cout << myArray_1[i] << " ";
		cout << myArray_1[N - 1];
	}

上面的代码用来判断是插入排序还是归并排序。

在归并排序中我们这么判断:

int isMerge = 0;
void Merge_Sort(ElementType A[], int N)
{
	int length;
	ElementType *TmpA;

	length = 1; // 初始化子序列长
	TmpA = (ElementType*)malloc(N * sizeof(ElementType));
	if (TmpA != NULL) {
		while (length < N) {
			Merge_pass(A, TmpA, N, length);
			if (isMerge == 1) {
				for (int ii = 0;ii < N;ii++)A[ii] = TmpA[ii];
				break;
			}
			if (isSame(TmpA, myArray2, N)) {
				isMerge = 1;
			}

			length *= 2;
			Merge_pass(TmpA, A, N, length);
			if (isMerge == 1) {
				break;
			}
			if (isSame(TmpA, myArray2, N)) {
				isMerge = 1;
			}
			length *= 2;
		}
		free(TmpA);
	}
	else printf("空间不足");
}

因为每个while循环实际上归并了两次,一次是归并到A中,另一次是归并到TmpA中,所以要分开处理。

完整代码如下:

#include <iostream>
using namespace std;

#define MaxVertexNum 105   //最大100个数据,多留出5个空白
typedef int ElementType;
ElementType myArray[MaxVertexNum];//存原数据,插入排序用
ElementType myArray_1[MaxVertexNum];//存原数据,归并排序用
ElementType myArray2[MaxVertexNum];//存第二行数据

int isInsertFlag = 0;
bool isSame(ElementType A[], ElementType B[], int N) {
	for (int i = 0;i < N;i++) {
		if (A[i] != B[i])return false;
	}
	return true;
}


void InsertionSort(ElementType A[], int N)
{ 
	// 插入排序 
	int P, i;
	ElementType Tmp;
	int 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 (flag == 1)break;
		if (isSame(A, myArray2, N)) {
			flag = 1;
			isInsertFlag = 1;
			cout << "Insertion Sort"<<endl;
		}
	}
}

// 归并排序 - 递归实现 
// L = 左边起始位置, R = 右边起始位置, RightEnd = 右边终点位置
void Merge(ElementType A[], ElementType TmpA[], int L, int R, int RightEnd)
{ // 将有序的A[L]~A[R-1]和A[R]~A[RightEnd]归并成一个有序序列
	int LeftEnd, NumElements, Tmp;
	int i;

	LeftEnd = R - 1; // 左边终点位置 
	Tmp = L;         // 有序序列的起始位置 
	NumElements = RightEnd - L + 1;

	while (L <= LeftEnd && R <= RightEnd) {
		if (A[L] <= A[R])
			TmpA[Tmp++] = A[L++]; // 将左边元素复制到TmpA 
		else
			TmpA[Tmp++] = A[R++]; // 将右边元素复制到TmpA 
	}

	while (L <= LeftEnd)
		TmpA[Tmp++] = A[L++]; // 直接复制左边剩下的 
	while (R <= RightEnd)
		TmpA[Tmp++] = A[R++]; // 直接复制右边剩下的 

	for (i = 0; i < NumElements; i++, RightEnd--)
		A[RightEnd] = TmpA[RightEnd]; // 将有序的TmpA[]复制回A[] 
}
// 归并排序 - 循环实现 
// 这里Merge函数在递归版本中给出 
// length = 当前有序子列的长度

void Merge_pass(ElementType A[], ElementType TmpA[], int N, int length)
{ // 两两归并相邻有序子列 
	int i, j;

	for (i = 0; i <= N - 2 * length; i += 2 * length)
		Merge(A, TmpA, i, i + length, i + 2 * length - 1);
	if (i + length < N) // 归并最后2个子列
		Merge(A, TmpA, i, i + length, N - 1);
	else // 最后只剩1个子列
		for (j = i; j < N; j++) TmpA[j] = A[j];

}
int isMerge = 0;
void Merge_Sort(ElementType A[], int N)
{
	int length;
	ElementType *TmpA;

	length = 1; // 初始化子序列长
	TmpA = (ElementType*)malloc(N * sizeof(ElementType));
	if (TmpA != NULL) {
		while (length < N) {
			Merge_pass(A, TmpA, N, length);
			if (isMerge == 1) {
				for (int ii = 0;ii < N;ii++)A[ii] = TmpA[ii];
				break;
			}
			if (isSame(TmpA, myArray2, N)) {
				isMerge = 1;
			}

			length *= 2;
			Merge_pass(TmpA, A, N, length);
			if (isMerge == 1) {
				break;
			}
			if (isSame(TmpA, myArray2, N)) {
				isMerge = 1;
			}
			length *= 2;
		}
		free(TmpA);
	}
	else printf("空间不足");
}

int main(void) {
	int N;cin >> N;
	for (int i = 0;i < N;i++) {
		cin >> myArray[i];
		myArray_1[i] = myArray[i];
	}
		
	for (int i = 0;i < N;i++)
		cin >> myArray2[i];

	InsertionSort(myArray, N);//插入排序
	if (isInsertFlag) {	
		for (int i = 0;i < N - 1;i++)
			cout << myArray[i] << " ";
		cout << myArray[N - 1];
	}	
	else if (!isInsertFlag) {
		cout << "Merge Sort" << endl;
		Merge_Sort(myArray_1, N);
		for (int i = 0;i < N - 1;i++)
			cout << myArray_1[i] << " ";
		cout << myArray_1[N - 1];
	}

	system("pause");
	return 0;
}



测试结果:

发布了187 篇原创文章 · 获赞 418 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/tiao_god/article/details/105499321