输入一个长度为 n的整数数列,从小到大输出前 m 小的数。
输入格式
第一行包含整数 n和 m。
第二行包含 n个整数,表示整数数列。
输出格式
共一行,包含 m个整数,表示整数数列中前 m小的数。
数据范围
1≤m≤n≤100000,
1≤数列中元素≤1000000000
输入样例:
5 3
4 5 1 3 2
输出样例:
1 2 3
//如何手写一个堆
//1.插入一个数 head[++size] = x;up(size);
//2.求集合当中的最小值 head[1];
//3.删除最小值 head[1] = head[size];size--;down(1);
//4.删除任意一个元素 head[p] = head[size] ;size--;down(k),up(k);
//5.修改任意一个元素 head [k] = x;down(k);up(k);
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
int n,m;
int h[N],cnt;
//down操作
void down(int u)
{
int t = u;
//首先它的左孩子的在我们的范围内,在判断它与左孩子的大小
if(u*2<=cnt&&h[u*2]<h[t]) t=u*2;
//再判断它与右孩子之间的大小
if(u*2+1<=cnt&&h[u*2+1]<h[t]) t=u*2+1;
//其实就是判断它是否小于它的左右孩子,如果不是,就进行交换
if(u!=t)
{
swap(h[u],h[t]);
down(t);
}
}
int main()
{
//先把数输入到堆中
cin >> n >> m;
for(int i=1;i<=n;i++) cin >> h[i];
//记录当前堆最后一个位置
cnt = n;
//只需对半处理,堆就能排好序
for(int i =n/2;i;i--) down(i);
//进行m次询问
while(m--)
{
//最小的数即为堆的开始元素
cout << h[1] << ' ';
//输出后将堆的开始元素删除
//因为顶点元素不好删除,我们将顶点元素转换为最后一个元素,通过cnt--删除
//转换完之后,我们顶点元素就变成我们最后一个元素,我们只需对顶点元素进行一边down操作就OK
h[1] = h[cnt];
cnt--;
down(1);
}
return 0;
}
样例演示
输入的原序列:4 5 1 3 2
首先进行堆的初始化:for(int i =n/2;i;i--) down(i);
当i=2时 u=2,t=2
h[u*2]<h[t] t=u*2=4
h[u*2+1]<h[t] t=u*2+1=5
u!=t swap(h[u],h[t])
交换后序列为:4 2 1 3 5
继续down(t) t=5,u=5
因为u*2>cnt u*2+1>cnt
u=t 结束
原序列变为:4 2 1 3 5
当i=1时 u=1,t=1
h[u*2]<h[t] t=u*2=2
h[u*2+1]<h[t] t=u*2+1=3
u!=t swap(h[u],h[t])
交换后的序列为:1 2 4 3 5
继续down(t)
因为u*2>cnt u*2+1>cnt
u=t 结束
原序列变为:1 2 4 3 5
输出环节:
输出h[1]:1
原序列变为:5 2 4 3
Down(1)
原序列变为:2 5 4 3
Down(2)
原序列变为:2 3 4 5(结束)
输出h[2]:2
原序列变为:5 3 4
Down(1)
原序列变为:3 5 4(结束)
输出h[3]:3
原序列变为:4 5;
Down(1) 还是4 5
输出h[4]:4
输出h[5]:5