携手创作,共同成长!这是我参与「掘金日新计划 · 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个元素的最小值(或最大值)。
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;
}