1

题目大意:

给定一个待排序序列和排序若干次的序列,要求判断是得到目标序列是通过插入排序还是堆排序,然后在输出再排序一次后的序列即可。

算法思路:

这里考察的就是插入排序和堆排序的完整排序的过程,按部就班的来就好。

插入排序:

首先将序列划分为有序序列部分和无序序列部分,初始有序序列为序列中第一个元素,对于长度为N的序列,插入排序会经过N-1趟排序,完成将N-1个无序序列的元素依次插入到有序序列之中,那么可以看到,插入排序分为外部循环控制趟数,内部循环负责找到插入位置,每次循环使用t暂存待插入元素,然后在$[1,j]$(有序序列)中查询待插入位置loc,只要$t>=temp[j]$,说明找到插入位置$j+1$,退出内层循环,将$j+1$位置的元素赋值为t即可。

堆排序:

由于目标序列是递增系列,那么我们需要建立大根堆,我们初始序列作为大根堆的层序遍历序列,然后从最后一个非叶子节点到根节点,依次向下调整,只要孩子节点中有比当期父节点大的,就交换孩子节点和父节点,并且向下依次递推查询,直到孩子节点的权重均比父节点小停止调整。这样就完成了初始大根堆的建立过程。堆排序就是将根节点和最后一个节点(未排序节点)进行交换,然后再向下调整根节点。这样就完成了一次堆排序的过程,每一次堆排序都会固定一个最大的元素在最后。

由于题目要求判断已排序部分的序列是插入排序还是堆排序的结果之上再进行一次排序,那么我们使用$same$标志已排序的部分序列是否是由当前的排序算法得到的,如果在排序完成后,判断$same$为$true$,那么就说明可以退出排序过程,进行相应的输出。如果$same$为$false$,那么就使用$isSame$函数更新$same$的值。

注意点:

  • 1、堆排序的根节点下标为1.
  • 2、插入排序的初始插入位置默认为1,是为了处理像3 1这种特殊情况。
  • 3、初始序列不参与和部分有序序列的比较,一定得先排序一次以后再比较是否相同。
  • 4、这里题目要求是目标朝着递增序列的方向排序,也即是最后的结果是从小到大,这里得注意堆排序是大根堆排序后才是递增序列,小根堆排序后是递减序列,因为每次都是从根节点和最后一个结点交换,最大(大根堆)或者最小(小根堆)的结点都在后面.

提交结果:

image.png

AC代码:

#include <cstdio>
#include <algorithm>

using namespace std;

int N;// 节点个数
int init[105];//初始序列
int temp[105];//暂存初始序列
int partial_sorted[105];//部分排序序列

bool isSame(){
    for (int i = 1; i <= N; ++i) {
        if(temp[i]!=partial_sorted[i]){
            return false;
        }
    }
    return true;
}

bool InsertionSort(){
    bool same = false;
    // N-1轮排序
    for (int i = 1; i < N; ++i) {
        int t = temp[i+1];//暂存待插入元素
        int loc = 1;// 待插入位置,初始为1,考虑3 1的特殊情况
        for (int j = i; j >= 1; --j) {
            if(t<temp[j]){
                temp[j+1] = temp[j];
            } else {
                // 找到插入位置
                loc = j+1;
                break;
            }
        }
        temp[loc] = t;
        if(same){
            // 在相等的情况下,又排序了一次
            break;
        }
        if(isSame()) same = true;//已经相同了,再排序一次
    }
    return same;
}

// 调整[low,high]的节点权重,low从最后一个非叶节点开始一直到1,high为当前堆需要调整的最后一个元素
void downAdjust(int low,int high){
    int i = low;
    int j = 2*i;//i的左孩子
    while (j<=high){
        // 只要左孩子一直存在,就向下调整
        if(j+1<=high&&temp[j+1]>temp[j]){
            // 右孩子节点更小
            j = j+1;
        }
        if(temp[j]>temp[i]){
            // 孩子节点的值更小
            swap(temp[j],temp[i]);
            i = j;
            j = 2*i;
        }else {
            // 孩子权重更加小,就停止比较
            break;
        }
    }
}

void createHeap(){
    for(int i=N/2;i>=1;--i){
        downAdjust(i,N);
    }
}

void print(){
    for(int i=1;i<=N;++i){
        printf("%d",temp[i]);
        if(i<N) printf(" ");
    }
}

void HeapSort(){
    createHeap();//先建堆
    // 将最后的节点和堆顶的节点交换,然后调整
    bool same = false;
    for(int i=N;i>=2;--i){
        swap(temp[i],temp[1]);
        downAdjust(1,i-1);
        if(same){
            printf("Heap Sort\n");
            print();
            break;
        }
        if(isSame()){
            same = true;
        }
    }
}

int main()
{
    scanf("%d",&N);
    for (int i = 1; i <= N; ++i) {
        scanf("%d",&init[i]);
        temp[i] = init[i];
    }
    for (int i = 1; i <= N; ++i) {
        scanf("%d",&partial_sorted[i]);
    }
    if(InsertionSort()){
        printf("Insertion Sort\n");
        print();
    } else {
        // 重置temp
        for (int i = 1; i <= N; ++i) {
            temp[i] = init[i];
        }
        HeapSort();
    }
    return 0;
}

乔梓鑫
569 声望17 粉丝

主要分享个人学习经验和心得