优先队列解析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_36783389/article/details/82425059

优先队列可以在加入元素和弹出元素时并行完成排序的效果,这个排序不光可以理解成大小的含义还可以的优先级高低的含义,所以这种数据类型在有关优先级的相关操作中使用的十分广泛,它不仅使用方便而且效率高可以大大降低相关操作的时间

 

一、优先队列的实现

优先队列的实现中,我们可以选择堆数据结构,最大优先队列可以选用大堆,最小优先队列可以选用小堆来实现,堆就是如下图的二叉树。下面以最大优先队列来讲解其原理。最大优先队列一般包括将一个元素插入到集合S中、返回集合S中具有最大key的元素、返回并删除集合S中具有最大key的元素等。

插入操作

  插入操作是将一个元素插入到集合S中,首先把该元素放入所有元素的下一位置,然后执行“上浮”操作,如下图示例(注意,下图示例是小堆,不过原理是一样的,图片来自深入理解Java PriorityQueue)

)PriorityQueue_offer.png

移除操作

  优先队列中,在队列非空情况下移除集合中第一个元素,也就是下标为0的元素,然后将集合中最后一个元素移到下标为0位置,在将下标为0的新元素执行“下沉”操作。如下图示例(注意,下图示例是小堆,不过原理是一样的,图片来自深入理解Java PriorityQueue)

PriorityQueue_poll.png

 

 

二、优先队列的相关定义与操作

 

一、相关定义

优先队列容器与队列一样,只能从队尾插入元素,从队首删除元素。但是它有一个特性,就是队列中最大的元素总是位于队首,所以出队时,并非按照先进先出的原则进行,而是将当前队列中最大的元素出队。这点类似于给队列里的元素进行了由大到小的顺序排序。元素的比较规则默认按元素值由大到小排序,可以重载“<”操作符来重新定义比较规则。

优先级队列可以用向量(vector)或双向队列(deque)来实现(注意list container不能用来实现queue,因为list的迭代器不是任意存取iterator,而pop中用到堆排序时是要求randomaccess iterator 的!):
priority_queue<vector<int>, less<int> > pq1;     // 使用递增less<int>函数对象排序
priority_queue<deque<int>, greater<int> > pq2;   // 使用递减greater<int>函数对象排序
其成员函数有“判空(empty)” 、“尺寸(Size)” 、“栈顶元素(top)” 、“压栈(push)” 、“弹栈(pop)”等。

其实C++中优先队列的实现也可以使用自定义cmp函数然后再引入cmp函数初始化优先队列的方法

样例

//自定义优先队列less比较函数
struct cmp
{
    bool operator()(int &a, int &b) const
    {
        //因为优先出列判定为!cmp,所以反向定义实现最小值优先
        return d[a] > d[b];
    }
};
priority_queue<int, std::vector<int>, cmp>

 

二、priority_queue

基本操作:

empty()      如果队列为空,则返回真

pop()    删除对顶元素,删除第一个元素

push()        加入一个元素

size()      返回优先队列中拥有的元素个数

top()     返回优先队列对顶元素,返回优先队列中有最高优先级的元素

在默认的优先队列中,优先级高的先出队。在默认的int型中先出队的为较大的数。

使用时需引入头文件:

#include <queue>

 

 

三、优先队列使用样例

参考NYOJ第55题

http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=55

懒省事的小明

时间限制:3000 ms  |  内存限制:65535 KB

难度:3

描述

      小明很想吃果子,正好果园果子熟了。在果园里,小明已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。小明决定把所有的果子合成一堆。 因为小明比较懒,为了省力气,小明开始想点子了:
  每一次合并,小明可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了。小明在合并果子时总共消耗的体力等于每次合并所耗体力之和。 
  因为还要花大力气把这些果子搬回家,所以小明在合并果子时要尽可能地节省体力。假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使小明耗费的体力最少,并输出这个最小的体力耗费值。 
  例如有3种果子,数目依次为1,2,9。可以先将1、2堆合并,新堆数目为3,耗费体力为3。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为12,耗费体力为12。所以小明总共耗费体力=3+12=15。可以证明15为最小的体力耗费值。

输入

第一行输入整数N(0<N<=10)表示测试数据组数。接下来每组测试数据输入包括两行,第一行是一个整数n(1<=n<=12000),表示果子的种类数。第二行包含n个整数,用空格分隔,第i个整数ai(1<=ai<=20000)是第i种果子的数目。

输出

每组测试数据输出包括一行,这一行只包含一个整数,也就是最小的体力耗费值。

样例输入

1
3 
1 2 9

样例输出

15

该题思路清晰但是如果使用模拟的操作去合并果子会超时这里使用优先队列即可通过

ac代码

#include <cstdio>
#include <queue>
using namespace std;

typedef long long ll;

int t,n,x;
priority_queue<ll,vector<ll>,greater<ll> > priority_queue1;

int main()
{
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i=0;i<n;i++){
            scanf("%d",&x);
            priority_queue1.push(x);
        }

        ll ans = 0;
        while (priority_queue1.size()!=1){
            int min1 = priority_queue1.top();
            priority_queue1.pop();
            int min2 = priority_queue1.top();
            priority_queue1.pop();
            priority_queue1.push(min1+min2);
            ans+=min1+min2;
        }

        printf("%lld\n",ans);

        while(!priority_queue1.empty()){
            priority_queue1.pop();
        }

    }

    return 0;
}

 

参考文章:

https://www.cnblogs.com/luoxn28/p/5616101.html

https://blog.csdn.net/luomingjun12315/article/details/47376359

https://www.cnblogs.com/xzxl/p/7266404.html

https://www.cnblogs.com/cielosun/p/5654595.html

展开阅读全文

没有更多推荐了,返回首页