二叉堆知识总结及相关题目题解

一、二叉堆性质:
1.数据结构是一颗完全二叉树
2.任意一个节点小于父节点称为”大根堆“,任意节点小于父节点称为”小根堆"

二、使用数组实现的小根堆二叉堆操作:

1.向上调整:
此时该节点不满足二叉堆性质,则通过与父节点交换来满足二叉堆性质,然后调整父节点,不断向上调整,使其满足性质为止.

void Up(int v)
{
    while(v>1)
    {
        if(a[v/2]>a[v])
        {
            swap(a[v/2],a[v]);
            v=v/2;
        }
        else
        {
            return ;
        }
    }
}

2.向下调整:
此时该节点不满足二叉堆性质,则通过与子节点交换来满足二叉堆性质,然后调整子节点,不断向下调整,使其满足性质为止.

void Down(int v)
{
    while(v*2<ji)
    {
        int k=v*2, val=a[v*2];
        if(v*2+1<ji&&a[v*2+1]<val)
        {
            k=v*2+1;
            val=a[v*2+1];
        }
        if(a[v]>val)
        {
            swap(a[v],a[k]);
            v=k;
        }
        else
        {
            return ;
        }
    }
}

3.插入:
向二叉堆插入一个节点时将该节点插入到末尾,然后向上调整。

void Add(int x)
{
    a[ji++]=x;
    Up(ji-1);
}

4.删除根节点

void Remove()
{
    ji--;
    a[1]=a[ji];
    Down(1);
}

三、题目
题目一:Supermarket(poj1456)
题意:
在这里插入图片描述

思路:
每个商品按过期时间升序排列,然后用小根堆维护,大小最大为当前时间,最后还在堆中的值的和为结果.

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef unsigned long long ull;

const ll inf=2e18;

int ji=1, a[1000005];

struct w
{
    int p, t;
} w[1000005];

bool cmp(struct w a,struct w b)
{
    return a.t<b.t;
}

void Down(int v)
{
    while(v*2<ji)
    {
        int k=v*2, val=a[v*2];
        if(v*2+1<ji&&a[v*2+1]<val)
        {
            k=v*2+1;
            val=a[v*2+1];
        }
        if(a[v]>val)
        {
            swap(a[v],a[k]);
            v=k;
        }
        else
        {
            return ;
        }
    }
}

void Up(int v)
{
    while(v>1)
    {
        if(a[v/2]>a[v])
        {
            swap(a[v/2],a[v]);
            v=v/2;
        }
        else
        {
            return ;
        }
    }
}

void Remove()
{
    ji--;
    a[1]=a[ji];
    Down(1);
}

void Add(int x)
{
    a[ji++]=x;
    Up(ji-1);
}

int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        ji=1;
        for(int i=1; i<=n; i++)
            scanf("%d%d",&w[i].p,&w[i].t);
        sort(w+1,w+n+1,cmp);
        int o=1,i=1;
        for(; i<=n&&o<=n; o++)
        {
            while(i<=n&&w[i].t<=o)
            {
                if(ji<=o||w[i].p>a[1])
                {
                    if(ji>o)
                    {
                        Remove();
                        Add(w[i].p);
                    }
                    else
                    {
                        Add(w[i].p);
                    }
                }
                i++;
            }
        }
        while(o>n&&i<=n)
        {
            if(ji<=n||w[i].p>a[1])
            {
                if(ji>n)
                {
                    Remove();
                    Add(w[i].p);
                }
                else
                {
                    Add(w[i].p);
                }
            }
            i++;
        }
        ll ans=0;
        //printf("%d\n",ji);
        for(int i=1; i<ji; i++)
        {
            ans=ans+a[i];
        }
        cout << ans << endl;
    }
}

题目二:Sequence(poj2442)
题目:
在这里插入图片描述
思路:用大根堆维护最小的n个数,按行操作.

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef unsigned long long ull;

const ll inf=2e18;

ll ji=1, a[1000005], b[1000005], c[1000005];

void Down(int v)
{
    while(v*2<ji)
    {
        int k=v*2, val=a[v*2];
        if(v*2+1<ji&&a[v*2+1]>val)
        {
            k=v*2+1;
            val=a[v*2+1];
        }
        if(a[v]<val)
        {
            swap(a[v],a[k]);
            v=k;
        }
        else
        {
            return ;
        }
    }
}

void Up(int v)
{
    while(v>1)
    {
        if(a[v/2]<a[v])
        {
            swap(a[v/2],a[v]);
            v=v/2;
        }
        else
        {
            return ;
        }
    }
}

void Remove()
{
    ji--;
    a[1]=a[ji];
    Down(1);
}

void Add(int x)
{
    a[ji++]=x;
    Up(ji-1);
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n, m;
        scanf("%d%d",&n,&m);
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=m; j++)
            {
                scanf("%lld",&b[j]);
            }
            if(i==1)
            {
                ji=1;
                for(int j=1; j<=m; j++)
                {
                    Add(b[j]);
                }
            }
            else
            {
                for(int j=1; j<=m; j++)
                {
                    c[j]=a[j];
                }
                ji=1;
                for(int j=1; j<=m; j++)
                {
                    for(int o=1; o<=m; o++)
                    {
                        if(ji<=m||a[1]>b[j]+c[o])
                        {
                            if(ji>m)
                            {
                                Remove();
                            }
                            //printf("%lld\n",b[j]+c[o]);
                            Add(b[j]+c[o]);
                        }
                    }
                }
            }
        }
        sort(a+1,a+ji);
        for(int i=1; i<ji; i++)
        {
            printf("%lld%c",a[i],i+1==ji?'\n':' ');
        }
    }
}

题目三:数据备份(Acwing147)
题目:
在这里插入图片描述
思路:双向链表加小根堆,可反悔堆.

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef unsigned long long ull;

const ll inf=1e9+7;

int s[100005];

int head=-1, pre[200005], Next[200005], val[200005], ji=1;

struct w
{
    int v, val;
} w, w2;

bool operator < (struct w a,struct w b)
{
    return a.val>b.val;
}

int main()
{
    int n, k;
    scanf("%d%d",&n,&k);
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&s[i]);
    }
    for(int i=2; i<=n; i++)
    {
        val[ji]=s[i]-s[i-1];
        pre[ji]=-1;
        Next[ji]=head;
        if(head!=-1)
            pre[head]=ji;
        head=ji;
        ji++;
    }
    priority_queue<struct w> q;
    ll ans=0;
    for(int i=head; i!=-1; i=Next[i])
    {
        w.v=i;
        w.val=val[i];
        q.push(w);
    }
    while(k--)
    {
        w.v=-1;
        while(w.v==-1||val[w.v]==-1)
        {
            w=q.top();
            q.pop();
        }
        ans+=w.val;
        //printf("%d %d\n",w.v,ans);
        if(pre[w.v]!=-1&&Next[w.v]!=-1)
        {
            w2.v=ji;
            w2.val=val[pre[w.v]]+val[Next[w.v]]-val[w.v];
            val[ji]=w2.val;
            val[pre[w.v]]=-1;
            val[Next[w.v]]=-1;
            pre[ji]=pre[pre[w.v]];
            if(pre[pre[w.v]]==-1)
            {
                head=ji;
            }
            else
            {
                Next[pre[pre[w.v]]]=ji;
            }
            Next[ji]=Next[Next[w.v]];
            if(Next[Next[w.v]]!=-1)
            {
                pre[Next[Next[w.v]]]=ji;
            }
            ji++;
            q.push(w2);
        }
        else if(pre[w.v]==-1&&Next[w.v]!=-1)
        {
            val[Next[w.v]]=-1;
            head=Next[Next[w.v]];
            if(Next[Next[w.v]]!=-1)
            {
                pre[Next[Next[w.v]]]=-1;
            }
        }
        else if(pre[w.v]!=-1&&Next[w.v]==-1)
        {
            val[pre[w.v]]=-1;
            if(pre[pre[w.v]]==-1)
            {
                head=-1;
            }
            else
            {
                Next[pre[pre[w.v]]]=-1;
            }
        }
        else
        {
            head=-1;
        }
    }
    cout << ans << endl;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值