1098 Insertion or Heap Sort(堆排序解释)(PAT甲级)

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

 堆排序

堆排序是对选择排序的改进,利用数据结构来排序。

 核心步骤

1.编写向下过滤函数percDown

2.建立最大堆

3.每次将堆顶(即最大值)与最后一个元素交换,然后“抛弃”最后一个元素,知道将最大堆转换成升序数组

 一、percDown函数 (下滤)和最大堆的建立

我们根据父节点和子节点的大小关系来判断是否需要交换父子结点,使得满足最大堆序。

由于最大堆一定是完全二叉树,所以我们可以用数组存储数据。

由于需要满足每个父节点都比起子节点大,自然的想到,从最后一个父节点开始做判断。

那么传入的第一个父节点的下标就是(len / 2 - 1) 其中len是数组长度。

 如上图所示,我们找到的最后一个父节点就是A[1](4/2-1),它有左右结点,且右节点大于左节点,

为了使交换后的父节点大于两个结点,所以我们要找到最大的子节点,这里我们选择右节点和父节点交换。于是得到下图

显然当前二叉树并不是最大堆,我们还要考虑到根节点和其子节点的大小关系,那么我们就要设计一个循环,从最后一个父节点开始,一直到根节点结束,每个结点作为参数传入percDown函数

第二步得到

此时会发现,A[1]又不满足最大堆序,解决的办法也就明确了,使用循环

综上所述,可以得到percDown函数的实现代码如下 其中start即传入的父节点,N为元素个数

void percDown(vector<int> & vec , int start , int N){
    int father , child;
    father = start;
    child = father * 2 + 1;
    while(child < N){
    //若右儿子存在且比左儿子大,让child指向右儿子
         if(child + 1 < N && vec[child] < vec[child + 1]){
            child ++;
        }
    //若父节点比子节点大,则满足最大堆序,退出函数
        if(vec[father] > vec[child]){
            return;
        }else{
    //否则交换父子结点 , 并让子节点和孙子结点继续递推上面的过程。
            swap(vec[father] , vec[child]);
            father = child;
            child = father * 2 + 1;
        }
    }
}

执行完循环之后,得到最大堆

二、通过交换和下滤完成排序

得到最大堆之后,堆顶即最大值,我们将堆顶和最后一个元素进行交换,可以把最大值放到最后。

 我们的策略是得到次大值,然后放在倒数第二个位置,以此类推...

因此,可以在将元素进行交换后,把最后一个元素“抛弃”,此时仅考虑剩下的4个元素,

显然最大堆的顺序被破坏了,于是我们再次调用percDown函数,因为除了根节点其他结点都已经满足最大堆的顺序了,所以我们把根节点传入percDown函数,N的值是当前元素减1。

这样我们就把剩下的4个元素重新排成了最大堆,然后循环控制,重复刚才的操作。

最终得到排好序的序列

本题中只需要分别在插入排序和堆排序的循环中增加判断检测语句即可。

 三、AC代码

#include<bits/stdc++.h>
using namespace std;

void Isinsertion(vector<int> & vec); //是否插入排序
void store(vector<int> & vec , int N); //读取数据
void percDown(vector<int> & vec, int start , int N); //向下过滤,形成最大堆
void Isheapsort(vector<int> & vec); //是否堆排序
bool isSame(vector<int> a , vector<int> b); //是否与给出的排序序列相同

vector<int> sorted;//题目给出的已排序数组

int main(){
    vector<int> pre; //原始数组
    vector<int> cpy; //备份数组
    int N;
    cin >> N;
    store(pre , N);
    cpy = pre;
    store(sorted , N);
    Isinsertion(pre);
    Isheapsort(cpy);
    return 0;
}

void store(vector<int> & vec , int N){
    for(int i = 0;i < N;i ++){
        int num;
        cin >> num;
        vec.emplace_back(num);
    }
}

bool isSame(vector<int> a , vector<int> b){
    for(int i = 0;i < a.size();i ++){
        if(a[i] != b[i]){
            return false;
        }
    }
    return true;
}

void Isinsertion(vector<int> & vec){
    int flag = 0;
    int i , j;
    for(i = 1;i < vec.size();i ++){
        int tmp = vec[i];
        for(j = i;j > 0 && vec[j - 1] >= tmp;j --){
            swap(vec[j] , vec[j - 1]);
        }
        vec[j] = tmp;
        if(flag == 1){
            for(int j = 0;j < vec.size();j ++){
                cout << vec[j];
                if(j != vec.size() - 1){
                    cout << " ";
                }
            }
            return;
        }
        if(isSame(vec , sorted)){
            cout << "Insertion Sort\n";
            flag = 1;
        }
    }
}

void percDown(vector<int> & vec , int start , int N){
    int father , child;
    father = start;
    child = father * 2 + 1;
    while(child < N){
    //若右儿子存在且比左儿子大,让child指向右儿子
         if(child + 1 < N && vec[child] < vec[child + 1]){
            child ++;
        }
    //若父节点比子节点大,则满足最大堆序,退出函数
        if(vec[father] > vec[child]){
            return;
        }else{
    //否则交换父子结点 , 并让子节点和孙子结点继续递推上面的过程。
            swap(vec[father] , vec[child]);
            father = child;
            child = father * 2 + 1;
        }
    }
}

void Isheapsort(vector<int> & vec){
    int flag = 0;
    int len = vec.size();
    //从最后一个父节点到根节点依次下滤,形成最大堆
    for(int i = len / 2 - 1;i >= 0;i --){
        percDown(vec , i , len);
    }
    //每次交换堆顶和最后一个元素,并抛弃最后一个元素,使得数组从右往左递减
    for(int i = len - 1;i > 0;i --){
        swap(vec[i] , vec[0]);
        percDown(vec , 0 , i);
        if(flag == 1){
            for(int j = 0;j < len;j ++){
                cout << vec[j];
                if(j != len - 1){
                    cout << " ";
                }
            }
            return;
        }
        //若某一步做完后,与给定排序序列相同,则记下标志,再做一次排序
        if(isSame(vec , sorted)){
            cout << "Heap Sort\n";
            flag = 1;
        }
    }
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

R_1220

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值