1.问题:
建堆时,为什么要从 n / 2 开始 ~ 0 结束 。 为什么不能从0开始到 n / 2 结束呢?
首先要明确要进行down操作时必须满足左儿子和右儿子已经是个堆。
开始创建堆的时候,元素是随机插入的,所以不能从根节点开始down,而是要找到满足下面三个性质的结点:
1.左右儿子满足堆的性质。
2.下标最大(因为要往上遍历)
3.不是叶结点(叶节点一定满足堆的性质)
笔记链接:https://www.acwing.com/solution/content/6362/
题目
838.堆排序
题目描述
输入一个长度为 n 的整数数列,从小到大输出前 m 小的数。
输入格式
第一行包含整数 n 和 m。
第二行包含 n 个整数,表示整数数列。
输出格式
共一行,包含 m 个整数,表示整数数列中前 m 小的数。
数据范围
1≤m≤n≤105,
1≤数列中元素≤109
样例
输入样例:
5 3
4 5 1 3 2
输出样例:
1 2 3
C++ 代码
# include <iostream>
using namespace std;
const int N = 1e5 + 10;
int e[N];
int n,m;
void up(int i)
{
while(i / 2 && e[i / 2] > e[i])
{
swap(e[i / 2] , e[i]);
i = i / 2;
}
}
void down(int i)
{
int t = i;
if(2 * i <= n && e[t] > e[2 * i])
{
t = 2 * i;
}
if(2 * i + 1 <= n && e[t] > e[i * 2 + 1])
{
t = i * 2 + 1;
}
if(t != i)
{
swap(e[i],e[t]);
down(t);
}
}
int main()
{
scanf("%d %d",&n,&m);
for(int i = 1 ; i <= n ; i++)
{
scanf("%d",&e[i]);
}
for(int i = n / 2 ; i >= 0 ; i--)
{
down(i);
}
while(m--)
{
printf("%d ",e[1]);
e[1] = e[n];
n--;
down(1);
}
return 0;
}