1、概念
堆是一种能高效找最值的数据结构,适合频繁的插入、取值操作;而堆排序就是利用它来排序的过程。可分为大根堆和小根堆。
大根堆:每个节点的值大于其左右节点的值;小根堆:每个节点的值小于其左右节点的值。
2、实战例子
给定一个数组,输出数组的前m个最小值。利用堆结构。(快排、归并也可以实现)
// 向下调整函数:将第 x 个结点向下调整,恢复小根堆性质
void hp(int x)
{
int t = x; // t 保存当前最小值的位置(初始为自身)
// 如果左子结点存在,且比当前值更小,则更新 t
if(2 * x <= s && q[t] > q[2 * x]) t = 2 * x;
// 如果右子结点存在,且比当前最小值更小,则更新 t(这里是和最新t比较)
if(2 * x + 1 <= s && q[2 * x + 1] < q[t]) t = 2 * x + 1;
// 如果 t 被更新了,说明子节点中有更小的值,需要交换并递归调整
if(x != t)
{
swap(q[x], q[t]); // 交换当前节点与更小的子节点
hp(t); // 继续递归调整被换下去的子节点
}
}
int main()
{
int n, m;
cin >> n >> m; // 输入 n 个元素和 m 次弹出操作
s = n; // 初始堆的大小为 n
// 读入堆的元素,下标从 1 开始
for (int i = 1; i <= n; i++) cin >> q[i];
// 从最后一个非叶子节点开始向下调整,建堆
for (int i = n / 2; i; i--) hp(i);
// 执行 m 次“弹出最小值”的操作
while (m--)
{
cout << q[1] << ' '; // 输出堆顶(最小值)
q[1] = q[s--]; // 用最后一个元素替换堆顶,并减小堆的大小
hp(1); // 向下调整堆顶
}
return 0;
}
难点分析:
1、为什么要n/2?确保从堆的底部开始比较;
2、q[1]=q[s --]作用?顶部元素输出之后就没用了,换个元素;