P 1035 插入与归并
转跳点 :🐏
1035 插入与归并 (25分)
根据维基百科的定义:
插入排序是迭代算法,逐一获得输入数据,逐步产生有序的输出序列。每步迭代中,算法从输入序列中取出一元素,将之插入有序序列中正确的位置。如此迭代直到全部元素有序。
归并排序进行如下迭代操作:首先将原始序列看成 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
这道题比较有意思,需要用到归并排序和插入排序的知识,两种排序的博客我都写了,自行转跳。
这道题有两种解法,
第一种是写两个排序算法,每执行完一轮就比较一次,时间复杂度 T(O) = O(N^3+N^2logN),太暴力了
第二种是利用插入排序的特性:插入排序,前面是排好序的,后面是与初始序列相同。只要符合这个条件的就是插入,不符合就是归并
用第一种方法暴力就行,但是第二种就需要对归并和插入排序十分熟悉,因为要写一个单步的归并和插入。插入的单步十分容易实现只要知道第几个没排好,继承索引,找到位置插入就行了。而归并就有些麻烦,需要完完整整的归并。
AC代码
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 int Merge = 0; 6 void InsertSorted(int arr[], int mid[], int n); 7 void MergeSorted(int arr[], int mid[], int n); 8 int ArrayEqual(int arr[], int ary[], int n); 9 void ArrayPrint(int arr[], int n); 10 11 int main(void) 12 { 13 static int n; 14 int arr[101]; 15 int ary[101]; 16 int mid[101]; 17 18 scanf("%d", &n); 19 20 for (int i = 0; i < n; i++) 21 { 22 scanf("%d", &arr[i]); 23 ary[i] = arr[i]; 24 } 25 26 for (int i = 0; i < n; i++) 27 { 28 scanf("%d", &mid[i]); 29 } 30 31 InsertSorted(arr, mid, n); 32 if (Merge) 33 { 34 MergeSorted(ary, mid, n); 35 } 36 37 return 0; 38 } 39 40 //插入排序 41 void InsertSorted(int arr[], int mid[], int n) 42 { 43 int j, temp; 44 45 for (int i = 1; i < n; i++) 46 { 47 j = i; 48 temp = arr[i]; 49 while (j > 0 && temp < arr[j - 1]) 50 { 51 arr[j] = arr[j - 1]; 52 j--; 53 } 54 arr[j] = temp; 55 56 if (ArrayEqual(arr, mid, n)) 57 { 58 j = ++i; 59 temp = arr[i]; 60 while (j > 0 && temp < arr[j - 1]) 61 { 62 arr[j] = arr[j - 1]; 63 j--; 64 } 65 arr[j] = temp; 66 printf("Insertion Sort\n"); 67 ArrayPrint(arr, n); 68 Merge = 0; 69 return; 70 } 71 } 72 Merge = 1; 73 } 74 75 void MergeAdd(int arr[], int left, int mid, int right, int *tmp) 76 { 77 int i = left; 78 int j = mid + 1; 79 int k = left; 80 while (i <= mid && j <= right) 81 { 82 tmp[k++] = arr[i] < arr[j] ? arr[i++] : arr[j++]; 83 } 84 while (i <= mid) 85 { 86 tmp[k++] = arr[i++]; 87 } 88 while (j <= right) 89 { 90 tmp[k++] = arr[j++]; 91 } 92 //把tmp中的内容拷给arr数组中 93 //进行归并的时候,处理的区间是arr[left,right),对应的会把 94 //这部分区间的数组填到tmp[left,right)区间上 95 memcpy(arr + left, tmp + left, sizeof(int) * (right - left + 1)); 96 } 97 98 void MergeSort(int arr[], int len, int *tmp, int mid[], int n) 99 { 100 if (len <= 1) 101 { 102 return; 103 } 104 int flag = 0; 105 106 //定义一个步长gap,初始值为1,相当于每次只合并两个长度为1的元素 107 for (int gap = 1; gap <= len; gap *= 2) 108 { 109 for (int i = 0; i <= len; i += 2 * gap) 110 { 111 int beg = i; 112 int mid = (gap - 1) + i; 113 if (mid >= len) 114 { 115 mid = len; 116 } 117 int end = mid + gap; 118 if (end >= len) 119 { 120 end = len; 121 } 122 MergeAdd(arr, beg, mid, end, tmp); 123 } 124 if (flag) 125 { 126 printf("Merge Sort\n"); 127 ArrayPrint(arr, n); 128 return; 129 } 130 if (ArrayEqual(arr, mid, n)) 131 { 132 flag = 1; 133 } 134 } 135 } 136 //归并排序接口 137 void MergeSorted(int arr[], int mid[], int n) 138 { 139 int *temp = malloc(sizeof(int) * n); 140 MergeSort(arr, n - 1, temp, mid, n); 141 free(temp); 142 } 143 //输出数组 144 void ArrayPrint(int arr[], int n) 145 { 146 for (int i = 0; i < n; i++) 147 { 148 printf("%d%s", arr[i], n - 1 == i ? "" : " "); 149 } 150 } 151 //判断相等 152 int ArrayEqual(int arr[], int ary[], int n) 153 { 154 for (int i = 0; i < n; i++) 155 { 156 if (arr[i] != ary[i]) 157 { 158 return 0; 159 } 160 } 161 return 1; 162 }
第二种:
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 int isinsertion(int n, int num[], int mid[]); 5 int nextmerge(int n, int num[], int mid[]); 6 int cmp(const void *a, const void *b) { return (*(int *)a) > (*(int *)b); } 7 8 int main() 9 { 10 int n, num[100], mid[100]; 11 int i; 12 scanf("%d", &n); 13 for (i = 0; i < n; i++) 14 { 15 scanf("%d", &num[i]); 16 } 17 for (i = 0; i < n; i++) 18 { 19 scanf("%d", &mid[i]); 20 } 21 22 if (isinsertion(n, num, mid)) 23 { 24 nextmerge(n, num, mid); 25 } 26 27 for (i = 0; i < n; i++) 28 { 29 printf("%d%s", num[i], i == n - 1 ? "" : " "); 30 } 31 return 0; 32 } 33 34 int isinsertion(int n, int num[], int mid[]) 35 { 36 int i, lenth; 37 for (i = 0; i < n - 1 && mid[i] <= mid[i + 1]; i++) 38 {//前面是排好序的 39 continue; 40 } 41 for (i++, lenth = i; i < n && mid[i] == num[i]; i++) 42 {//后面是与初始序列相同 43 continue; 44 } 45 if (i < n) 46 { 47 return 1; 48 } 49 printf("Insertion Sort\n"); 50 lenth++; 51 qsort(num, lenth, sizeof(int), cmp); 52 return 0; 53 } 54 55 int nextmerge(int n, int num[], int mid[]) 56 { 57 int i, j, lenth; 58 printf("Merge Sort\n"); 59 for (lenth = 1, i = 0; i < n && lenth <= n; lenth *= 2) 60 { //只能一步步归并 61 for (i = 0; i < n && num[i] == mid[i]; i++) 62 ; 63 for (j = 0; j < n / lenth; j++) 64 { 65 qsort(num + j * lenth, lenth, sizeof(int), cmp); 66 } 67 qsort(num + j * lenth, n % lenth, sizeof(int), cmp); 68 } 69 return 0; 70 }
偷懒了,用qsort”实现“归并
PTA不易,诸君共勉!
大道五十,天衍四九,人遁其一!