浅谈C++ STL中的优先队列(priority_queue)

从我以前的博文能看出来,我是一个队列爱好者,很多并不是一定需要用队列实现的算法我也会采用队列实现,主要是由于队列和人的直觉思维的一致性导致的。

今天讲一讲优先队列(priority_queue),实际上,它的本质就是一个heap,我从STL中扒出了它的实现代码,大家可以参考一下。

首先函数在头文件<queue>中,归属于命名空间std,使用的时候需要注意。

队列有两种常用的声明方式:

std::priority_queue<T> pq;
std::priority_queue<T, std::vector<T>, cmp> pq;

第一种实现方式较为常用,接下来我给出STL中的对应声明,再加以解释。

template<class _Ty,
    class _Container = vector<_Ty>,
    class _Pr = less<typename _Container::value_type> >
    class priority_queue

大家可以看到,默认模板有三个参数,第一个是优先队列处理的类,第二个参数比较有特点,是容纳优先队列的容器。实际上,优先队列是由这个容器+C语言中关于heap的相关操作实现的。这个容器默认是vector,也可以是dequeue,因为后者功能更强大,而性能相对于vector较差,考虑到包装在优先队列后,后者功能并不能很好发挥,所以一般选择vector来做这个容器。第三个参数比较重要,支持一个比较结构,默认是less,默认情况下,会选择第一个参数决定的类的<运算符来做这个比较函数。

接下来开始坑爹了,虽然用的是less结构,然而,队列的出队顺序却是greater的先出!就是说,这里这个参数其实很傲娇,表示的意思是如果!cmp,则先出列,不管这样实现的目的是啥,大家只能接受这个实现。实际上,这里的第三个参数可以更换成greater,像下面这样:

std::priority_queue<T, std::vector<T>, greater<T>> pq;

一般大家如果是自定义类就干脆重载<号时注意下方向了,没人在这里麻烦,这个选择基本上是在使用int类还想小值先出列时。

从上面的剖析我们也就知道了,想要让自定义类能够使用优先队列,我们要重载小于号。

class Student
{
    int id;
    char name[20];
    bool gender;
    bool operator < (Student &a) const
    {
        return id > a.id;
    }
};

就拿这个例子说,我们想让id小的先出列,怎么办,就要很违和的给这个小于符号重载成实际上是大于的定义。

如果我们不使用自定义类,又要用非默认方法去排序怎么办?就比如说在Dijkstra中,我们当然不会用点的序号去排列,无论是正序还是反序,我们想用点到起点的距离这个值来进行排序,我们怎样做呢?细心的读者在阅读我的有关Dijkstra那篇文章时应该就发现了做法——自定义比较结构。优先队列默认使用的是小于结构,而上文的做法是为我们的自定义类去定义新的小于结构来符合优先队列,我们当然也可以自定义比较结构。自定义方法以及使用如下,我直接用Dijkstra那篇的代码来说明:

int cost[MAX_V][MAX_V];
int d[MAX_V], V, s;
//自定义优先队列less比较函数
struct cmp
{
    bool operator()(int &a, int &b) const
    {
        //因为优先出列判定为!cmp,所以反向定义实现最小值优先
        return d[a] > d[b];
    }
};
void Dijkstra()
{
    std::priority_queue<int, std::vector<int>, cmp> pq;
    pq.push(s);
    d[s] = 0;
    while (!pq.empty())
    {
        int tmp = pq.top();pq.pop();
        for (int i = 0;i < V;++i)
        {
            if (d[i] > d[tmp] + cost[tmp][i])
            {
                d[i] = d[tmp] + cost[tmp][i];
                pq.push(i);
            }
        }
    }
}

优先队列的日常使用,了解上面那些就已经足够。下面给出优先队列的所有成员函数的STL实现方法,希望你看完没有一脸卧槽的感觉。c就是你声明时候的那个vector或者其他容器。

    void push(value_type&& _Val)
        {    // insert element at beginning
        c.push_back(_STD move(_Val));
        push_heap(c.begin(), c.end(), comp);
        }

    template<class... _Valty>
        void emplace(_Valty&&... _Val)
        {    // insert element at beginning
        c.emplace_back(_STD forward<_Valty>(_Val)...);
        push_heap(c.begin(), c.end(), comp);
        }

    bool empty() const
        {    // test if queue is empty
        return (c.empty());
        }

    size_type size() const
        {    // return length of queue
        return (c.size());
        }

    const_reference top() const
        {    // return highest-priority element
        return (c.front());
        }

    void push(const value_type& _Val)
        {    // insert value in priority order
        c.push_back(_Val);
        push_heap(c.begin(), c.end(), comp);
        }

    void pop()
        {    // erase highest-priority element
        pop_heap(c.begin(), c.end(), comp);
        c.pop_back();
        }
 
时间: 2024-10-12 10:43:58

浅谈C++ STL中的优先队列(priority_queue)的相关文章

浅谈C++ STL deque 容器

浅谈C++ STL deque 容器 本篇随笔简单介绍一下\(C++STL\)中\(deque\)容器的使用方法及常见使用技巧. deque容器的概念 \(deque\)的意义是:双端队列.队列是我们常用而且必须需要掌握的数据结构.\(C++STL\)中的确有模拟队列的模板:#include<queue>中的\(queue\)和\(priority\_queue\).队列的性质是先进先出,即从队尾入队,从队首出队.而\(deque\)的特点则是双端进出,即处于双端队列中的元素既可以从队首进/出

浅谈C++ STL stack 容器

浅谈C++ STL stack 容器 本篇随笔简单介绍一下\(C++STL\)中\(stack\)容器的使用方法和常见的使用技巧. stack容器的概念 \(stack\)在英文中是栈的意思.栈是一种基本的数据结构.而\(C++STL\)中的栈就是把这种数据结构模板化了. 栈的示意图如下:这是一个先进后出的数据结构.这非常重要!! 事实上,\(stack\)容器并不是一种标准的数据结构,它其实是一个容器适配器,里面还可以存其他的\(STL\)容器.但那种使用方法过于高深而且不是很常用,所以在此不

浅谈C++ STL queue 容器

浅谈C++ STL queue 容器 本篇随笔简单介绍一下\(C++STL\)中\(queue\)容器的使用方法和常见的使用技巧.\(queue\)容器是\(C++STL\)的一种比较基本的容器.我们在学习这个容器的时候,不仅要学到这个容器具体的使用方法,更要从中体会\(C++STL\)的概念. queue容器的概念 \(queue\)在英文中是队列的意思.队列是一种基本的数据结构.而\(C++STL\)中的队列就是把这种数据结构模板化了.我们可以在脑中想象买票时人们站的排队队列.我们发现,在一

浅谈二维中的树状数组与线段树

一般来说,树状数组可以实现的东西线段树均可胜任,实际应用中也是如此.但是在二维中,线段树的操作变得太过复杂,更新子矩阵时第一维的lazy标记更是麻烦到不行. 但是树状数组在某些询问中又无法胜任,如最值等不符合区间减法的询问.此时就需要根据线段树与树状数组的优缺点来选择了. 做一下基本操作的对比,如下图. 因为线段树为自上向下更新,从而可以使用lazy标记使得矩阵的更新变的高校起来,几个不足就是代码长,代码长和代码长. 对于将将矩阵内元素变为某个值,因为树状数组自下向上更新,且要满足区间加法等限制

转: 浅谈C/C++中的指针和数组(二)

转自:http://www.cnblogs.com/dolphin0520/archive/2011/11/09/2242419.html 浅谈C/C++中的指针和数组(二) 前面已经讨论了指针和数组的一些区别,然而在某些情况下,指针和数组是等同的,下面讨论一下什么时候指针和数组是相同的. C语言标准对此作了说明: 规则1:表达式中的数组名被编译器当做一个指向该数组第一个元素的指针: 注:下面几种情况例外 1)数组名作为sizeof的操作数 2)使用&取数组的地址 规则2:下标总是与指针的偏移量

转:浅谈C/C++中的指针和数组(一)

转自:http://www.cnblogs.com/dolphin0520/archive/2011/11/09/2242138.html 浅谈C/C++中的指针和数组(一) 指针是C/C++的精华,而指针和数组又是一对欢喜冤家,很多时候我们并不能很好的区分指针和数组,对于刚毕业的计算机系的本科生很少有人能够熟练掌握指针以及数组的用法和区别.造成这种原因可能跟现在大学教学以及现在市面上流行的很多C或者C++教程有关,这些教程虽然通俗易懂,但是在很多关键性的地方却避而不谈或者根本阐述不清楚,甚至很

浅谈C语言中的联合体(转载)

联合体union 当多个数据需要共享内存或者多个数据每次只取其一时,可以利用联合体(union).在C Programming Language 一书中对于联合体是这么描述的: 1)联合体是一个结构: 2)它的所有成员相对于基地址的偏移量都为0: 3)此结构空间要大到足够容纳最"宽"的成员: 4)其对齐方式要适合其中所有的成员: 下面解释这四条描述: 由于联合体中的所有成员是共享一段内存的,因此每个成员的存放首地址相对于于联合体变量的基地址的偏移量为0,即所有成员的首地址都是一样的.为

浅谈深度学习中潜藏的稀疏表达

浅谈深度学习中潜藏的稀疏表达 “王杨卢骆当时体,轻薄为文哂未休. 尔曹身与名俱灭,不废江河万古流.” — 唐 杜甫<戏为六绝句>(其二) [不要为我为啥放这首在开头,千人千面千理解吧] 深度学习:概述和一孔之见 深度学习(DL),或说深度神经网络(DNN), 作为传统机器学习中神经网络(NN).感知机(perceptron)模型的扩展延伸,正掀起铺天盖地的热潮.DNN火箭般的研究速度,在短短数年内带来了能“读懂”照片内容的图像识别系统,能和人对话到毫无PS痕迹的语音助手,能击败围棋世界冠军.引

浅谈C语言中的强符号、弱符号、强引用和弱引用

摘自http://www.jb51.net/article/56924.htm 浅谈C语言中的强符号.弱符号.强引用和弱引用 投稿:hebedich 字体:[增加 减小] 类型:转载 时间:2014-10-31 我要评论 这篇文章主要介绍了C语言中的强符号.弱符号.强引用和弱引用的定义及相关内容,非常的简单易懂,有需要的朋友可以参考下 首先我表示很悲剧,在看<程序员的自我修养--链接.装载与库>之前我竟不知道C有强符号.弱符号.强引用和弱引用.在看到3.5.5节弱符号和强符号时,我感觉有些困惑