AcWing 838. 堆排序

112 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情

AcWing 838. 堆排序

输入一个长度为 n 的整数数列,从小到大输出前 m 小的数。

输入格式

第一行包含整数 n 和 m。

第二行包含 n 个整数,表示整数数列。

输出格式

共一行,包含 m 个整数,表示整数数列中前 m 小的数。

数据范围

1 ≤ m ≤ n ≤ 10^5,
1 ≤ 数列中元素 ≤ 10^9

输入样例:

5 3
4 5 1 3 2

输出样例:

1 2 3

思路

模拟堆,使用堆的特性进行排序

堆(Heap)是计算机科学中一类特殊的数据结构,是最高效的优先级

堆(heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质: 堆中某个结点的值总是不大于或不小于其父结点的值; 堆总是一棵完全二叉树。 将根结点最大的堆叫做最大堆或大根堆,根结点最小的堆叫做最小堆或小根堆。常见的堆有二叉堆、斐波那契堆等。 堆是非线性数据结构,相当于一维数组,有两个直接后继。 堆的定义如下:n个元素的序列{k1,k2,ki,…,kn}当且仅当满足下关系时,称之为堆。 且或者, 若将和此次序列对应的一维数组(即以一维数组作此序列的存储结构)看成是一个完全二叉树,则堆的含义表明,完全二叉树中所有非终端结点的值均不大于(或不小于)其左、右孩子结点的值。由此,若序列{k1,k2,…,kn}是堆,则堆顶元素(或完全二叉树的根)必为序列中n个元素的最小值(或最大值)。

image.png

ac代码

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100001;
int arr[N],n,m;
void down(int u){
    int cnt = u;
    if(u*2<=n&&arr[u*2]<=arr[cnt]) cnt = u*2; //判断是否有左儿子,和判断是否小于左儿子
    if(u*2+1<=n&&arr[u*2+1]<=arr[cnt]) cnt =u*2+1;//判断是否有右儿子,判断是否小于右儿子
    if(cnt!=u){ //判断是否变换
        swap(arr[cnt],arr[u]); //交换两个数值
            down(cnt); //进行下一层判断
    }
}
int main(){
    cin >> n >> m;
    for(int i = 1;i <= n;i++) cin >> arr[i];
    for(int i = n / 2 ; i ;i--) down(i); //构建堆排序,操作为 O(n)
    while (m -- ){
        cin >> arr[1]; //每次输出堆顶(即为最小值)
        arr[1] = arr[n--]; //将堆顶删除,堆的最后一个值(即为最大值)覆盖掉最小值,size--
        down(1);//重新进行排序
    }
    return 0;
}