acwing839模拟堆 2021/11/26

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

  1. I x,插入一个数 xx;
  2. PM,输出当前集合中的最小值;
  3. DM,删除当前集合中的最小值(数据保证此时的最小值唯一);
  4. D k,删除第 kk 个插入的数;
  5. C k x,修改第 kk 个插入的数,将其变为 xx;

现在要进行 NN 次操作,对于所有第 22 个操作,输出当前集合的最小值。

输入格式

第一行包含整数 NN。

接下来 NN 行,每行包含一个操作指令,操作指令为 I xPMDMD k 或 C k x 中的一种。

输出格式

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

每个结果占一行。

数据范围

1≤N≤1051≤N≤105
−109≤x≤109−109≤x≤109
数据保证合法。

输入样例:

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

输出样例:

-10
6

 这道题首先要理解ph【】和hp【】数组的含义:

ph[]存的是堆里的下标(第k个插入的点是堆里的那个点)

hp[]存的是第k个插入的点(堆里的莫一个点是第几个插入的点)

上面两行是y总讲的原话,然后我的理解是,ph【】里面的坐标是第k个数,对应的值是堆里面的坐标;hp【】里面的坐标是堆里面的数,对应的值是插入的第k个数。

所以ph【】的值我对应的是sz(因为有size函数,所以我设置的变量是sz),hp【】的值我对应的是idx。

然后这个比堆排序多了个函数,eswap(end swap)这是我自己设的名字hh,调用的两个值a,b,是堆里面的坐标,不是插入的坐标,也就是sz而不是idx。

然后在交换q【】的值的之前,要把ph【】和hp【】的映射给交换,没有顺序可言,就是说不管是ph【hp【】】 , hp【】  还是hp【ph【】】,ph【】 ,都是一样的意义。

不要忘记把down和up的swap给改一下,然后我自己敲代码的时候,一直答案错误,分享一下我的错误是什么:

我在down的时候,以为t既然=u了,所以if里面u,t都是一样的,所以u和t我都写的t,然后发现答案是错的,为什么呢,因为有两个if,在第一个if成立的时候,t的值已经变了,在进行第二个if的时候,t * 2 就等于 u*4了。

然后简单讲解一下

I  插入的坐标和堆坐标都加一,q【sz】 = x,然后就是上面讲的ph【idx】= sz ,(里面idx插入坐标   对应 堆的坐标)然后up(sz)让这个新插入的数和它的父节点或祖宗节点比较交换。

PM 输出根节点

DM 交换根节点和最后一个点,整体sz-1,然后把根节点往下down

D 找出k的对应堆坐标,因为这里的k是第k个插入的数,可以自己理解一下,然后从k点down,up,往上往下都比较一遍

最后一个跟D同理

代码:

对了,下面strcmp里面是两个字符串比较,相等的话返回0,包含在cstring = string.h 的库中

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std ;

const int N = 1e5 + 50;

int idx = 0 ,sz = 0 , q[N] , ph[N] , hp[N];

void eswap(int a ,int b)
{
    swap(ph[hp[a]], ph[hp[b]]);//这里要交换映射关系,知道一个元素在堆中的位置从而通过ph[hp[a]映射出在数组中的位置i, j
    swap(hp[a], hp[b]);//交换在堆中的位置
    swap(q[a], q[b]);
}

void down(int u)
{
    int t = u ;
    if(u * 2 <= sz&& q[t] > q[u * 2]) t = u * 2 ;
    if(u * 2 + 1 <= sz && q[t] > q[u * 2 + 1 ]) t = u * 2 + 1 ;
    if(u!= t)
    {
        eswap(u, t) ;
        down(t);
    }
}
void up(int u)
{
    while(u / 2 && q[u/2] > q[u] )
    {
        eswap(u , u / 2);
        u = u / 2 ;
    }
}
int main()
{
    int n ;
    cin >> n ;
    while(n--)
    {
        char op[5] ;
        int k , x ;
        scanf("%s" , op);
        if(!strcmp(op,"I"))
        {
            cin >> x ;
            idx ++ ,sz ++ ;
            q[sz] = x ;
            ph[idx] = sz , hp[sz] = idx; 
            up(sz);
        }
        else if (!strcmp(op,"PM"))cout << q[1] << endl;
        else if (!strcmp(op,"DM"))
        {
            eswap(1 , sz) ;
            sz -- ;
            down(1);
        }
        else if (!strcmp(op,"D"))
        {
            cin >> k ;
            k = ph[k] ;
            eswap(k , sz) ;
            sz -- ;
            down(k) , up(k) ;
        }
        else
        {
            cin >> k >> x ;
            k = ph[k] ;
            q[k] = x ;
            down(k) , up(k);
        }
    }
    return 0 ;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三粒小金子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值