AcWing 839. 模拟堆

发布时间 2023-12-04 15:01:04作者: 爬行的蒟蒻

题解:
维护一个集合,初始时集合为空,支持如下几种操作:
I x,插入一个数 \(x\)
PM,输出当前集合中的最小值;
DM,删除当前集合中的最小值(数据保证此时的最小值唯一);
D k,删除第 \(k\) 个插入的数;
C k x,修改第 \(k\) 个插入的数,将其变为 \(x\)
现在要进行 \(N\) 次操作,对于所有第 \(2\) 个操作,输出当前集合的最小值。

原题链接:839. 模拟堆 - AcWing

堆是一种特殊的完全二叉树

优先级队列(小根堆的模拟实现)未央.303的博客-CSDN

小根堆的性质:每个结点的值都小于其左孩子和右孩子结点的值。(大根堆反之)

结点h[x]的左孩子为h[x * 2],右孩子为h[x * 2 + 1]

小根堆的两种基本操作(递归):

  1. 上升 up():只要当前结点小于其父节点,则将其与父节点交换;
  2. 下降 down():只要当前结点大于其孩子结点,则将其与孩子节点中的较小值交换;

堆的五种基本操作:

  1. 插入一个数heap[++size]=x; up(size);
    在末尾插入,随后让其上升到合适的位置。
  2. 求集合中的最小值heap[1];
    根节点一定为最小值。
  3. 删除最小值heap[1]=heap[size]; size--; down(1);
    数组的性质:删除第一个结点难,而删除最后一个结点易。
    令最后一个结点直接覆盖最小的根节点,比起直接删除第一个结点更方便。
  4. 删除任意一个元素heap[k]=heap[size]; size--; down(k); up(k);
    当删除堆中间的元素时,不能确定该节点和其父节点、左右孩子结点的关系;
    故同时执行down(k); up(k);操作,实际上只有其中一者会被执行。
  5. 修改任意一个元素heap[k]=x; down(k); up(k);

关键点:h hp ph 之间的映射关系

  • ph[k]=u:第 \(k\) 个点的下标为 \(u\)
  • hp[u]=k:节点 \(u\) 是第 \(k\) 个插入的
  • hp[u]=k,则ph[k]=u
#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10;
int n, cnt, siz;
int h[N];   //堆
int ph[N];  //ph[k]=u: 第k个点的下标为u
int hp[N];  //hp[u]=k: 节点u是第k个插入的

//映射关系:若hp[u]=k,则ph[k]=u
void heap_swap(int u, int v) {
    swap(h[u], h[v]);
    swap(hp[u], hp[v]);
    swap(ph[hp[u]], ph[hp[v]]);

}

void down(int u) {
	int tmp = u;
	//左孩子存在,且左孩子小于当前节点
	if (u * 2 <= len && h[u * 2] < h[tmp])
		tmp = u * 2;
	//右孩子存在,且右孩子小于当前节点和左孩子
	if (u * 2 + 1 <= len && h[u * 2 + 1] < h[tmp])
		tmp = u * 2 + 1;
	if (u != tmp) {	//根节点不是当前最小值
		swap(h[u], h[tmp]);
		down(tmp);	//递归
	}
}

void up(int u) {
    if (u / 2 > 0 && h[u / 2] > h[u]) {
        heap_swap(u, u / 2);
        up(u / 2);
    }
}

int main()
{
    cin >> n;
    while (n--)
    {
        string op;
        int k, x;
        cin >> op;
        if (op == "I") {
            cin >> x;
            h[++siz] = x;   //插入队尾
            cnt++;          //更新是第k个插入的数
            ph[cnt] = siz;  //记录第k个插入的数的下标
            hp[siz] = cnt;  //记录该点是第k个插入的
            up(siz);
        }
        else if (op == "PM") {
            cout << h[1] << endl;
        }
        else if (op == "DM") {
            heap_swap(1, siz--);
            down(1);
        }
        else if (op == "D") {
            cin >> k;
            int tmp = ph[k];
            heap_swap(tmp, siz--);
            up(tmp);
            down(tmp);
        }
        else if (op == "C"){
            cin >> k >> x;
            h[ph[k]] = x;
            down(ph[k]);
            up(ph[k]);
        }
    }
    return 0;
}