题解:P3378 【模板】堆

· · 题解

题解:【模板】堆

题目描述

给定一个数列,初始为空,请支持以下三种操作:

  1. 插入操作op=1):将整数 x 插入到数列中。
  2. 查询最小值op=2):输出数列中的最小值。
  3. 删除最小值op=3):删除数列中的最小值。如果有多个最小值,只删除一个;如果数列为空,输出 -1

数据范围: 操作次数 n \leq 10^6 ,输入整数 x < 2^{31}

解题思路

核心思想:使用二叉堆实现优先队列

本题可以使用小端堆(即堆顶元素为最小值的二叉堆)来高效实现插入、查询和删除操作。以下是具体实现细节。

数据结构:数组模拟二叉堆

核心操作

1. 插入操作(push

插入时,将新元素添加到堆的末尾,并通过自下而上的修复modify)维护堆的性质。

void modify(int x) {
    if (x == 1 || w[x] > w[x / 2]) return; // 如果满足小端堆性质,退出递归
    swap(w[x], w[x / 2]);                  // 否则交换当前节点与父节点
    modify(x / 2);                         // 继续修复父节点
}

void push(int x) {
    w[++tot] = x; // 将新元素插入堆尾
    modify(tot);  // 自下而上修复
}

2.查询最小值(top

堆顶元素即为最小值,直接返回堆顶即可。如果堆为空,则不进行操作。

int top() {
    if (tot) return w[1]; // 堆非空时返回堆顶元素
}

3.删除最小值(pop

删除时,将堆顶元素与堆尾元素交换后删除堆尾,并通过自上而下的修复repair)维护堆的性质。如果堆为空,输出 -1

void repair(int x) {
    if (x * 2 > tot) return; // 如果没有子节点,退出递归
    int tar = x * 2;         // 默认左儿子为目标节点
    if (x * 2 + 1 <= tot && w[x * 2 + 1] < w[x * 2]) tar = x * 2 + 1; // 找到更小的儿子
    swap(w[x], w[tar]);      // 交换当前节点与目标节点
    repair(tar);             // 继续修复目标节点
}

void pop() {
    if (tot) {
        swap(w[1], w[tot--]); // 将堆顶与堆尾交换并删除堆尾
        repair(1);            // 自上而下修复
    } else printf("-1\n");    // 如果堆为空,输出 -1
}

复杂度分析

空间复杂度