PTA 09-排序2 Insert or Merge 及 09-排序3 Insertion or Heap Sort 两道题目分析

20 篇文章 2 订阅
3 篇文章 0 订阅

PTA-mooc完整题目解析及AC代码库:PTA(拼题A)-浙江大学中国大学mooc数据结构全AC代码与题目解析(C语言)

这两道题比较类似,故放在一些进行分析。


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). 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

题目解析

题意分析

题目先给出数组元素长度,然后给出两个整型数组数据。第一行数组数据是乱序的输入待排序数据,第二行数组数据是进行了某种排序操作,但是并未排序完成的数据序列。

现在要求判断出第二行数组数据是经过了插入排序还是归并排序得到的,并且还要输出再多经过一次排序操作后的数据序列。

解法分析

两种排序操作中,插入排序具有一个特点:无论进行到哪一步,其数据中,前一部分一定是有序的,而后一部分未排序过的数据一定与原对应位置数据相同。相对而言,归并排序是否进行到某一步就比较难以判断,因此我们可以先判断第二个数组序列是否为插入排序的中间结果,若是则再多进行一次插入排序操作,否则当作归并排序处理即可。

在进行归并排序处理时,因为已经判断过是否为插入排序,所以这里无需再判断是否为归并排序了,只需要直接当作归并排序处理即可。归并排序的中间结果一定具有这样的特点:上一步归并区间长度一定小于等于当前结果中所有有序子序列长度中最小的那个。注意:这里的归并区间长度一定是2的指数,所以找到当前结果中所有有序子序列长度中最小的那个之后还需要进行判断。

看完老师解法后的质疑点

关于这道题我最开始自己做的时候没有发现太大问题,但是听完老师的解法之后,我又重新思考了一下这个题目,发现我有两个质疑点:

(1)题目的数据问题

如果输入数据为:

6
2 1 7 6 4 3
1 2 6 7 3 4

那么,该排序一定是归并排序。但是在这个数据中,既可以认为当前归并序列的长度为2(即2和1、7和6、4和3分别归并),也可以为4(即2、1、7、6进行归并,4和3进行归并),那么在求下一次归并排序结果时,输出数据是:1 2 6 7 3 4,还是:1 2 3 4 6 7,即下次归并序列长度是按4还是8来计算。按照老师给出的测试点来看,这个数据的输出结果为1 2 3 4 6 7,也就是说该数据可能是归并排序的多个中间排序结果时,以最后一次为主,但是题目中没有说明这一点。

(2)在归并排序时,找所有连续有序子列的最短长度的解法

老师在讲解中使用下例来证明了找有序子列最短长度的解法是不可行的

14
4 2 1 3 13 14 12 11 8 9 7 6 10 5
1 2 3 4 11 12 13 14 6 7 8 9 5 10

但是我觉得这种方法是可行的,并且我就是按照这种思路来实现的而且通过了所有测试点。只要在寻找过程中不考虑该数据中结尾处有序子列的长度即可。

但这样很容易产生疑问,如果有序子列的最短长度在末尾怎么办?因为归并排序的当前步的序列长度一定大于等于数据结尾处的有序子列长度,所以走到结尾之前找到的最短长度一定也大于等于结尾处的有序子列长度(否则不可能是归并排序),所以可以忽略数据最后的有序子列长度。

关于这两个疑问,我已经在课程讨论区提出,目前老师还没有给出相应解答。

代码

#include <stdio.h>
#include <stdlib.h>

typedef int ElementType;

ElementType *ReadInput(int N);
void solve(ElementType A[], ElementType ansArr[], int N);
void PrintArr(ElementType A[], int N);

void Merge(ElementType A[], ElementType TmpA[], int L, int R, int RightEnd);
void Merge_pass(ElementType A[], ElementType TmpA[], int N, int length);

int Check_Insertion_Sort(ElementType A[], ElementType ansArr[], int N);
void Merge_Sort(ElementType A[], ElementType ansArr[], int N);

int main()
{
    int N;
    ElementType *arr, *ansArr;
    scanf("%d", &N);
    arr = ReadInput(N);
    ansArr = ReadInput(N);
    solve(arr, ansArr, N);
    free(arr);
    free(ansArr);

    return 0;
}

ElementType *ReadInput(int N)
{
    ElementType *arr;
    int i;
    arr = (ElementType *)malloc(sizeof(ElementType) * N);
    for (i = 0 ; i < N; ++i)
        scanf("%d", &arr[i]);
    return arr;
}

void PrintArr(ElementType A[], int N)
{
    int i;
    for ( i = 0; i < N - 1; ++i)
        printf("%d ", A[i]);
    printf("%d", A[i]);
}

void solve(ElementType A[], ElementType ansArr[], int N)
{
    if (Check_Insertion_Sort(A, ansArr, N)) {   // 判断是否为插入排序
        printf("Insertion Sort\n");
        PrintArr(ansArr, N);
    }
    else {  // 此时一定是归并排序
        Merge_Sort(A, ansArr, N);
        printf("Merge Sort\n");
        PrintArr(A, N);
    }
}

void Merge(ElementType A[], ElementType TmpA[], int L, int R, int RightEnd)
{
    int LeftEnd, Tmp;

    LeftEnd = R - 1;
    Tmp = L;

    while (L <= LeftEnd && R <= RightEnd) {
        if (A[L] < A[R])
            TmpA[Tmp++] = A[L++];
        else
            TmpA[Tmp++] = A[R++];
    }
    while (L <= LeftEnd) TmpA[Tmp++] = A[L++];
    while (R <= RightEnd) TmpA[Tmp++] = A[R++];
}

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)
        Merge(A, TmpA, i, i + length, N - 1);
    else
        for (j = i; j < N; ++j) TmpA[j] = A[j];
}

void Merge_Sort(ElementType A[], ElementType ansArr[], int N)
{
    int i, small_length, length;

    small_length = N;
    length = 1;
    for (i = 0; i < N - 1; ++i) {
        if (ansArr[i] > ansArr[i + 1]) {
            small_length = length < small_length ? length : small_length;
            length = 1;
            continue;
        }
        ++length;
    }

    small_length -= small_length % 2;
    if (small_length < N)
        Merge_pass(ansArr, A, N, small_length);
}

int Check_Insertion_Sort(ElementType A[], ElementType ansArr[], int N)
{
    int i, pos, flag;
    ElementType tmp;

    flag = 1;
    for (i = 0; i < N - 1; ++i) {   // 找到第一个非有序位置
        if (ansArr[i] > ansArr[i + 1]) {
            pos = i + 1;
            break;
        }
    }
    for (i = pos; i < N; ++i) {     // 判断非有序部分是否和输入数据对应位置一样
        if (A[i] != ansArr[i]) {
            flag = 0;
            break;
        }
    }
    if (flag) { // 如果是插入排序,进行下一次插入排序操作
        tmp = ansArr[pos];
        for (; pos > 0 && tmp < ansArr[pos - 1]; --pos)
            ansArr[pos] = ansArr[pos - 1];
        ansArr[pos] = tmp;
    }
    return flag;
}


09-排序3 Insertion or Heap Sort

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.

Heap sort divides its input into a sorted and an unsorted region, and it iteratively shrinks the unsorted region by extracting the largest element and moving that to the sorted region. it involves the use of a heap data structure rather than a linear-time search to find the maximum.

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 “Heap 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 resulting 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 6 0
6 4 5 1 0 3 2 7 8 9

Sample Output 2:

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

题目解析

题意分析

与上一题完全相同,此题只不过将归并排序改为堆排序而已。

解法分析

此处还是先判断是否为插入排序,因此操作与上一题完全相同,不同之处在于确定为非插入排序之后的处理。

堆排序的中间结果特点是:数组数据最后部分是已经排好序的,而数组的第一个元素接下来要和最后的有序部分前面一个元素交换,然后重新调整堆。因此,我们只需要从后往前遍历数据,找到数组头元素要插入的位置然后进行元素交换及堆的调整即可。

代码

#include <stdio.h>
#include <stdlib.h>

typedef int ElementType;

ElementType *ReadInput(int N);
void solve(ElementType A[], ElementType ansArr[], int N);
void PrintArr(ElementType A[], int N);

void PercDown(ElementType A[], int p, int N);

int Check_Insertion_Sort(ElementType A[], ElementType ansArr[], int N);
void Heap_Sort(ElementType A[], int N);

int main()
{
    int N;
    ElementType *arr, *ansArr;
    scanf("%d", &N);
    arr = ReadInput(N);
    ansArr = ReadInput(N);
    solve(arr, ansArr, N);
    free(arr);
    free(ansArr);

    return 0;
}

ElementType *ReadInput(int N)
{
    ElementType *arr;
    int i;
    arr = (ElementType *)malloc(sizeof(ElementType) * N);
    for (i = 0 ; i < N; ++i)
        scanf("%d", &arr[i]);
    return arr;
}

void PrintArr(ElementType A[], int N)
{
    int i;
    for ( i = 0; i < N - 1; ++i)
        printf("%d ", A[i]);
    printf("%d", A[i]);
}

void solve(ElementType A[], ElementType ansArr[], int N)
{
    if (Check_Insertion_Sort(A, ansArr, N)) {   // 判断是否为插入排序
        printf("Insertion Sort\n");
        PrintArr(ansArr, N);
    }
    else {  // 此时一定是堆排序
        Heap_Sort(ansArr, N);
        printf("Heap Sort\n");
        PrintArr(ansArr, N);
    }
}

void PercDown(ElementType A[], int p, int N)
{
    int Parent, Child;
    ElementType X;

    X = A[p];
    for (Parent = p; (Parent * 2 + 1) < N; Parent = Child) {
        Child = Parent * 2 + 1;
        if ((Child != N - 1) && (A[Child] < A[Child + 1]))
            ++Child;
        if (X >= A[Child]) break;
        else
            A[Parent] = A[Child];
    }
    A[Parent] = X;
}

int Check_Insertion_Sort(ElementType A[], ElementType ansArr[], int N)
{
    int i, pos, flag;
    ElementType tmp;

    flag = 1;
    for (i = 0; i < N - 1; ++i) {   // 找到第一个非有序位置
        if (ansArr[i] > ansArr[i + 1]) {
            pos = i + 1;
            break;
        }
    }
    for (i = pos; i < N; ++i) {     // 判断非有序部分是否和输入数据对应位置一样
        if (A[i] != ansArr[i]) {
            flag = 0;
            break;
        }
    }
    if (flag) { // 如果是插入排序,进行下一次插入排序操作
        tmp = ansArr[pos];
        for (; pos > 0 && tmp < ansArr[pos - 1]; --pos)
            ansArr[pos] = ansArr[pos - 1];
        ansArr[pos] = tmp;
    }
    return flag;
}

void Heap_Sort(ElementType A[], int N)
{
    ElementType Tmp;
    int i;

    for (i = N - 1; i > 0 && A[i] >= A[0] ; --i) ;
    if (i != 0) {
        Tmp = A[0];
        A[0] = A[i];
        A[i] = Tmp;
        PercDown(A, 0, i);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值