一、优先级队列的介绍和使用
(1)介绍
翻译:
(1) 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
(2) 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。
(3)优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。
(4)底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作:
empty():检测容器是否为空
size():返回容器中有效元素个数
front():返回容器中第一个元素的引用
push_back():在容器尾部插入元素
pop_back():删除容器尾部元素
(5)标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector。
(6)需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数make_heap、push_heap和pop_heap来自动完成此操作。
其实优先级队列就是一个堆,只不过在STL中我们都把它叫做优先级队列,而把堆的算法归类于算法库中
(2)使用
优先级队列默认使用vector作为底层存储数据的容器,在vector上又实用了堆算法将vector中的元素造成堆的结构,因此Priority_queue就是堆,所有需要用到堆的地方都可以考虑使用priority_queue。注意:priority_queue默认情况下是大堆
可以看出来优先级队列在默认情况下是大堆,那么我们如何让他变成小堆呢?这就需要涉及到仿函数了,我们来看看。
我们把这里的第三个模板参数中的greater<int>就称为仿函数!
二、仿函数
那么何为仿函数呢?
仿函数最大的用途就是来替代函数指针,我们不妨回忆一下之前我们在c语言阶段如果要传一个函数指针,或者返回值是一个函数指针,是不是会因为函数指针的语法而非常头疼,但是有了仿函数后,就变得十分简单了。我们只需要写一个类,并且在类中仅仅只对()重载一个函数operator(),这样他的使用就会和普通的函数看起来一样了,但是却避免了函数指针的麻烦和易错性,并且在后续使用了模板的思想后,让仿函数的功能远超函数指针了。
下面我们来简单看看仿函数在优先级队列实现中的用处!
(1)如果要在其中存放自定义类型的数据,需要用户在自定义类中提供<或>的重载
但是这样也存在一个问题,如果priority_queue中存储的不是自定义类型的对象本身,而是他的指针呢?由于指针本身是内置类型,所以他在比较大小的时候比较的就是指针地址的大小了,而这却是一个非常不合理的问题,因为谁都不知道地址到底是怎么分配的!
这个时候我们可以自己写一个仿函数,来确定比较的方法,像这样就好了