超级详细模拟堆,常见错误及其思路汇总

本文详细介绍了小根堆的构建和操作,包括插入元素时的up操作和删除元素时的down操作,以及如何在堆中维护映射关系。在处理插入和删除操作时,需要注意特定的细节,如在删除时保存元素位置并在调整堆后更新映射。此外,文中提供了完整的C++代码示例来说明这些概念。
摘要由CSDN通过智能技术生成

题目描述 题目链接

 

 

题目分析

1.堆的操作只需要 down up swap 就可以实现

2.堆其实就是一个完全二叉数,分为小堆,大堆,他的性质是每一个节点u 的左儿子是2u,右儿子是2u+1;

3.小堆的父节点小于他的儿子,大堆的父节点大于他的儿子

4.小堆的操作中 down 的操作:

使用递归的方式进行down

具体代码:(小根堆)
void down(int u)                       //需要down的根节点
{
     int t=u;                             //t用来存储u的最小儿子          
     if(2*u<=len && h[u*2]<h[t]) t=u*2;
     if(2*u+1<=len && h[u*2+1]<h[t]) t=u*2+1;

     if(t!=u)                              //如果t不相等意味着u还不满足小堆的条件于最小儿子交            换后继续down
      {       
         h_swap(t,u);                      //当有映射关系时需要用这个自定义函数
         down(t);                          //递归
      }
}

5.小堆操作中 up 的操作:

同样使用递归或者使用y总的循环

具体代码:(递归小根堆)

    void up(int u)
    {
        int t=u;                                //up中的t保存的是父结点
        if(u/2 && h[u/2]>h[t]) t=u/2;           //up操作中只需要判断up儿子与根的大小就可

        if(t!=u)                                //递归操作
        {
            h_swap(t,u);
            up(t);
        }
    }

具体代码:(y总循环)

    void up(int u)
    {
        while(u/2 && h[u/2]>h[u])
        {
            h_swap(u/2,u);
            u/=2;
        }
    }

6.小堆中用up 和 down 完成插入 与删除操作

基本思想:
insert:将插入的元素放入最小根的底端然后进行up操作
delt:将删除元素t 与 最后一个元素交换 然后down(t)(这里的t是交换后最后一共元素现在所处的编号)len--

void insert(int u)
{
    h[++len]=u;
    up(len);
}

void delt(int k)
{
    h[k]=h[len--];
    down(k);
}

以上是常见堆的insert和delt
在题目模拟队中insert 和 delt的细节操作
让道古纠结了好久wa了一页(悲伤)
我在代码中独立出来了一定要看,就是下面
模拟堆的delt操作,那真是醉了

//先看8
下面是模拟堆insert和delt的操作:

    void insert(int u);
    {
        h[++len]=u;
        pk[len]=++idx;
        kp[idx]=len;
        up(len);
    }

    void delt(int k)
    {
        int u=kp[k];  
        h_swap(kp[k],len--);
        up(u);
        down(u);
    }

7.模拟堆中h_swap

具体代码+注释

    void h_swap(int a,int b)
    {
        swap(h[a],h[b]);            //交换数值
        swap(pk[a],pk[b]);          //交换pk映射
        swap(kp[pk[a]],kp[pk[b]]);  //交换kp映射
    }

8.堆的模拟中映射关系:
{
一般堆的定义为:
int h[maxn],kp[maxn],pk[maxn],idx,len;
h[maxn] 表示堆
kp[maxn] 表示堆 第 k 个数——> h中结点(point)编号的映射
pk[maxn] 表示堆 结点编号为 p 的 结点——> h中第 k 个数的映射
idx表示 已经插入 过 多少结点
//idx!=len
len表示堆中的所有结点的数量
}
9.小方法:
在构造堆的时候用这个比较快

for(int i=n/2;i;i--) down(i);

模拟堆 代码
#include<iostream>

using namespace std;

int const maxn =1e5+10;

int h[maxn],kp[maxn],pk[maxn],idx,len;

void h_swap(int a,int b)
{
    swap(h[a],h[b]);            //交换数值
    swap(pk[a],pk[b]);          //交换pk映射
    swap(kp[pk[a]],kp[pk[b]]);  //交换kp映射
}

void down(int u)                       //需要down的根节点
{
    int t=u;                             //t用来存储u的最小儿子          
    if(2*u<=len && h[u*2]<h[t]) t=u*2;
    if(2*u+1<=len && h[u*2+1]<h[t]) t=u*2+1;

    if(t!=u)                              //如果t不相等意味着u还不满足小堆的条件于最小儿子交换后继续down
    {       
        h_swap(t,u);                      //当有映射关系时需要用这个自定义函数
        down(t);                          //递归
    }
}

void up(int u)
{
    int t=u;                                //up中的t保存的是父结点
    if(u/2>0 && h[u/2]>h[t]) t=u/2;           //up操作中只需要判断up儿子与根的大小就可

    if(t!=u)                                //递归操作
    {
        h_swap(t,u);
        up(t);
    }
}

int main()
{
    int n; cin>>n;
    while(n--)
    {
         string aim; cin>>aim;
        if(aim=="I")
        {
            int x; cin>>x;
            //insert(int x)操作
            h[++len]=x;
            pk[len]=++idx;          
            kp[idx]=len;
            up(len);
        }
        else if(aim=="PM")
        {
            cout<<h[1]<<endl;
        }
        else if(aim=="DM")
        {
            h_swap(1,len--);
            down(1);
        }
        else if(aim=="D")
        {
            int k; cin>>k;




            int u=kp[k]; //一定要注意这个地方
                         /*
                          我之前是这么写的
                          {
                                h_swap(kp[k],len--)
                                up(kp[k]);
                                down(kp[k]);
                          }
                          这么写会在h_swap之后 kp[k]其实是len,所以要提前保存在u中
                         */



            h_swap(kp[k],len--);
            up(u);
            down(u);

        }
        else if(aim=="C")
        {
            int k,x; cin>>k>>x;
            h[kp[k]]=x;
            up(kp[k]);
            down(kp[k]);
        }
        //for(int i=1;i<=len;i++) cout<<h[i]<<" ";
       // cout<<"---------"<<endl;
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

道古九封

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

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

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

打赏作者

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

抵扣说明:

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

余额充值