算法模板之堆-优先队列

堆的定义

堆结构是一种数组对象,可以被视为一颗完全二叉树。
树中的每个节点与数组中存放该节点中值的那个元素相对应。

这就是一个小根堆,以及它在数组里面的存储
由上图不难看出

Father(i)=i/2

leftChild(i)=i2

rightChild(i)=i2+1

当然也可以用位运算来加快速度
Father(i)=i>>1

leftChild(i)=i<<1

由于i*2一定为偶数(i∈N+),所以i<<1|1等价于i*2+1.
rightChild(i)=i<<1|1

堆的性质

n个关键字序列L[1…n]称为堆,当且仅当该序列满足:
1. L(i)<=L(2i)且L(i)<=L(2i+1)或
2. L(i)>=L(2i)且L(i)>=L(2i+1)
满足第一个条件的成为小根堆(即每个结点值小于它的左右孩子结点值),满足第二个添加的成为大根堆(即每个结点值大于它的左右孩子结点值)。

堆的操作

1.向堆中加入一个元素(push),并保持堆的有序
2.从队中删除一个元素(pop),并保持堆的有序
3.查找最小/最大值(top).

算法分析

插入一个元素:
1.在堆尾加入一个元素,把这个节点设置为当前结点
2.维护——
比较当前结点和它的父结点的大小,小于父结点的话就交换它们的值,一直交换直到当前结点大于等于它的父结点(小根堆),大根堆相反。

删除一个元素
1.把堆的最后一个结点(heap[size])放到根的位置上(将根结点覆盖),pa指向根结点,size–。
2.根结点没有儿子就return;否则,取儿子中最小的一个与其比较,比子结点大就交换它们的值,pa指向子结点。(小根堆,大根堆则是取儿子中最大的比较,比子结点小就交换)

查找最小、最大值
就是堆顶元素

具体实现

插入操作

void push(int d)//向堆heap中插入d
{
    int now,next;
    heap[++size]=d;
    now=size;
    while(now>1)
    {
        next=now>>1;
        if(heap[now]>=heap[next]) return;//小根堆
        swap(heap[now],heap[next]);
        now=next;
    }
}

//或者是使用stl里面堆的维护操作:


void put(int d)
{
    heap[++size]=d;
    push_heap(heap+1,heap+1+size,greater()<int>);//小根堆
    //push_heap(heap+1,heap+1+size); 默认是大根堆
}

删除操作

void pop()
{
    int now=1,next;
    heap[1]=heap[size--];
    while((now<<1)<=size)
    {
        next=now<<1;
        if(next<size&&heap[next+1]<heap[next]) next++;
        if(heap[now]<=heap[next]) break;
        swap(heap[now],heap[next]);
        now=next;
    }
}

//使用stl:

void pop(int d)
{
    pop_heap(heap+1,heap+1+size,greater()<int>);
    //pop_heap(heap+1,heap+1+size); 这是大根堆
}

查找

int get()
{
    return heap[1];
}

当然以上操作也可以使用优先队列容器(priority_queue)


洛谷上关于小根堆的测试https://www.luogu.org/problemnew/show/3378
手写堆:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

const int N=1000000+121;
int size,n,heap[N];

void swap(int &a,int &b)
{
    int t=a;
    a=b,b=t;
}

void put(int d)
{
    int now,next;
    heap[++size]=d;
    now=size;
    while(now>1)
    {
        next=now>>1;
        if(heap[now]>=heap[next]) return;
        swap(heap[now],heap[next]);
        now=next;
    }
}

void pop()
{
    int now=1,next;
    heap[1]=heap[size--];
    while((now<<1)<=size)
    {
        next=now<<1;
        if(next<size&&heap[next+1]<heap[next]) next++;
        if(heap[now]<=heap[next]) break;
        swap(heap[now],heap[next]);
        now=next;
    }
}

int get()
{
    return heap[1];
}

int main()
{
    int a,b;
    scanf("%d",&n);
    while(n--)
    {
        scanf("%d",&a);
        if(a==1)
        {
            scanf("%d",&b);
            put(b);
        }
        else if(a==2)
            printf("%d\n",get());
        else pop();
    }
}

优先队列容器:

#include<iostream>
#include<cstdio>
#include<queue>//要用stl的队列或者优先队列的话不要忘了这个头文件
#include<algorithm>
using namespace std;

//定义一个优先队列h,greater<>表示为小根堆
//vector<>是向量,动态数组的一种,所占内存随插入元素的增加而增加
priority_queue<int,vector<int>,greater<int> >h;

int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int a;
        scanf("%d",&a);
        if(a==1)
        {
            scanf("%d",&a);
            h.push(a);//插入
        }
        else if(a==2)
            printf("%d\n",h.top());//堆顶元素
        else h.pop();//弹出
    }
}

堆的经典例题:
洛谷:P1090 合并果子

洛谷:P1631 序列合并

洛谷:P3378 【模板】小根堆

Openjudge: 3.7 集合问题

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值