思路:
分析难点:
i为什么从n/2开始down?
首先要明确要进行down操作时必须满足左儿子和右儿子已经是个堆。
开始创建堆的时候,元素是随机插入的,所以不能从根节点开始down,而是要找到满足下面三个性质的结点:
-
左右儿子满足堆的性质。
-
下标最大(因为要往上遍历)
-
不是叶结点(叶节点一定满足堆的性质)
那这个点为什么时n/2?看图。
代码:
# include<iostream>
# include<algorithm>
using namespace std;
const int N = 100010;
int h[N],cnt;
void down(int u)
{
int t = u;
if(cnt >= u * 2 && h[u * 2] < h[t]) t = u * 2;
if(cnt >= u * 2 + 1 && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
if(u != t)
{
swap(h[u],h[t]);
down(t);
}
}
int main()
{
int n,m;
scanf("%d %d",&n,&m);
cnt = n;
for(int i = 1;i <= n;i++)
{
scanf("%d",&h[i]);
}
for(int i = n / 2;i;i--) down(i);
while(m--)
{
printf("%d ",h[1]);//输出堆顶(最小值)
h[1] = h[cnt--];//删除堆顶(用最后一个数覆盖第一个数) 并且长度 减1
down(1);//让覆盖好的数往下面走
}
puts("");
return 0;
}