【每日算法】基础算法——模拟堆(三十四)

题目内容

维护一个集合,初始时集合为空,支持如下几种操作:

“I x”,插入一个数x;
“PM”,输出当前集合中的最小值;
“DM”,删除当前集合中的最小值(数据保证此时的最小值唯一);
“D k”,删除第k个插入的数;
“C k x”,修改第k个插入的数,将其变为x;
现在要进行N次操作,对于所有第2个操作,输出当前集合的最小值。

输入格式

第一行包含整数N。
接下来N行,每行包含一个操作指令,操作指令为”I x”,”PM”,”DM”,”D k”或”C k x”中的一种。

输出格式

对于每个输出指令“PM”,输出一个结果,表示当前集合中的最小值。
每个结果占一行。

数据范围

1≤N≤10^5
−10^9≤x≤10^9
数据保证合法。

输入样例

8
I -10
PM
I -10
D 1
C 2 8
I 6
PM
DM

输出样例

-10
6

题解

通过堆的基本操作,即实现交换堆中的两个元素的基础上进行的up操作和down操作就能够实现题目中给出的几个目标任务。

代码

#include <iostream>
#include <algorithm>
#include <string.h>

using namespace std;

const int N = 100010;

int h[N], ph[N], hp[N], cnt; //ph存储第k个插入的数在堆里的下标,hp存储堆中的某个点是第几个插入的元素。维护这样一个数据结构,能够快速找到符合题目要求的第k个插入元素的相关操作。

void heap_swap(int a, int b) //对维护的数组ph和hp也要进行交换
{
swap(ph[hp[a]],ph[hp[b]]);//交换ph中的两个点
swap(hp[a], hp[b]);//交换hp中的两个点
swap(h[a], h[b]);//交换了两个点
}

void down(int u)
{
int t = u;//设置一个临时变量
if (u * 2 <= cnt && h[u * 2] < h[t]) t = u * 2;//判断根节点与左子节点的大小关系,如果小则准备替换
if (u * 2 + 1 <= cnt && h[u * 2 + 1] < h[t]) t = u * 2 + 1;//判断根节点与右子节点的大小关系,如果小则准备替换
if (u != t)//如果上述判断存在,则u和t铁定不一样,那么进行替换,否则跳过
{
heap_swap(u, t);
down(t);
}
}

void up(int u)
{
while (u / 2 && h[u] < h[u / 2]) #和根节点进行比较即可,因为根节点一定最小,和最小的根节点比较,不会修改另一半的分支
{
heap_swap(u, u / 2);
u >>= 1;
}
}

int main()
{
int n, m = 0;
scanf("%d", &n);
while (n -- )
{
char op[5];
int k, x;
scanf("%s", op);
if (!strcmp(op, "I"))
{
scanf("%d", &x);
cnt ++ ;
m ++ ;
ph[m] = cnt, hp[cnt] = m;
h[cnt] = x;
up(cnt);
}
else if (!strcmp(op, "PM")) printf("%d\n", h[1]);
else if (!strcmp(op, "DM"))
{
heap_swap(1, cnt);
cnt -- ;
down(1);
}
else if (!strcmp(op, "D"))
{
scanf("%d", &k);
k = ph[k];
heap_swap(k, cnt);
cnt -- ;
up(k);
down(k);
}
else
{
scanf("%d%d", &k, &x);
k = ph[k];
h[k] = x;
up(k);
down(k);
}
}

return 0;
}
Author: Frederic Niu
Link: https://www.fredericniu.cn/2021/01/14/【每日算法】基础算法——模拟堆(三十四)/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
我的公众号