C++——优先级队列

作者:几冬雪来

时间:2023年10月10日

内容: C++——优先级队列与仿函数讲解

目录

前言:  

优先级队列: 

接口:

仿函数:

特殊用法(指针):

结尾: 



前言:  

在上一篇博客中我们讲解了stack和queue,以及deque的用法说明。而今天我们将借由stack和queue来学习C++的又一重要学习板块——优先级队列。 

优先级队列: 

要学习优先级队列就要了解什么是优先级队列。

在上一篇博客中我们学习了deque(双端队列),而deque和优先级队列有异曲同工之处,那就是二者虽然都有队列的名字,但是实际上它们都不是队列优先级队列写作——priority_queue

和stack的后进先出与queue的先进先出类似,优先级队列顾名思义就是要先出优先级高的。 

在优先级队列中它默认为大的优先级高

像上图一样,如果是大的优先级高那就类似二叉树中的大堆,如果是小的优先级高那么这个地方就类似二叉树的小堆。 

而且优先级队列并没有一个单独的头文件,我们只要书写了“#include<queue>”就可以使用优先级队列了。 

在上图我们就使用了优先级队列进行了数据的插入。 

与此同时,因为优先级队列也是适配器,因此它也没提供迭代器不支持遍历,我们只能栈和队列一样,边取边走才能取到全部的数据。  

再然后对pq里面的数据进行top和pop以后就能依次取到我们想要的值

在上面有说过,优先级队列可以分为大的优先级高的大堆,它也可以变成小的优先级高的小堆。那么小的优先级高的代码又应该怎么写呢? 

要实现小堆,这里就要用到之前学习的模板参数。第一条代码只有一个模板参数的是大堆的实现,下面的三个模板参数的则是小堆的实现

小堆的实现需要传第三个模板参数,这也是我们后面要讲解到的——仿函数控制实现小堆的操作。 

那么接下来就来看看优先级队列的代码实现

这里就是我们的代码。

第一步是借由函数模板来输入我们想要的数据

第二步则是建堆,找到最后叶子的父结点的位置。然后对其进行向下调整,i = (_con.size()-1-1)/2是找父结点的条件,第一个-1是找到下标,第二个是找到子结点,最后/2找到它的父结点

最后就是调整,这里要注意循环条件子结点必须在区间里面,还有就是对比和调换。

这里就完成了优先级队列代码的书写。 

同样的代码进行修改,我们也可以实现向上调整建立大堆

这里就将原先建小堆的parent和child计算方式改变一下,原先代码的parent和child的书写位置也进行调换

同时要注意向上调整的AdjustUp中循环的条件并不是单纯的将原先的child改为parent,因为parent的值是不会小于0的,因此条件要进行更改

接口:

接下来就是优先级队列一些常见的接口书写。

在这里我们主要关注的就是pop和push接口

pop的话要取最上面的数据我们不能直接pop,这里我们就要将堆顶元素和最后一个子结点交换。然后再进行pop最后一个子结点的数据,最后进行一次向下调整

然后就是push的插入数据,这个地方也是在最后的位置插入一个数据,接下来进行向上调整即可完成

仿函数:

接下来我们来学习C++一个重要的知识点——仿函数

那么仿函数是什么呢?在这里我们书写一个最简单的仿函数。

这就是我们最简单的仿函数代码的书写,也可以叫它为函数对象

但是严格意义上来讲仿函数和函数对象并不是完全相同的,仿函数更多的是指这个类,函数对象则是指这个类所定义的对象

接下来我们再写一个代码。

  

在这里如果单看lessfunc而不去看上面的类,我们大概率会将它理解为是一个函数名或者函数指针。 

但是实际上并不是这样的,结合上面的类来看。在类中重载了运算符,这个重载的运算符就是operator()

这个地方本质等价于——lessfunc.operator()

而在这里我们就可以看出来仿函数到底是什么作用了,它的作用就是让类的对象可以像使用函数那样被使用。 

那么在这个地方有人就要问了,既然仿函数的作用是让类的对象可以像函数那样使用的话,为什么不直接写一个函数呢

这是因为直接写一个函数的话,它是被我们写死的。要让其可以修改有两种方法,一种是指针,但是指针在有些地方的可读性差。而另外一种就是仿函数,这也就是仿函数出现的理由

在优先级队列中,因为运算符重载的原因,我们在类模板处增加模板参数。

而在这里Comaper就是我们的仿函数。 

用我们优先级队列的代码进行修改

在原代码中我们比较都是直接大于号或者小于号进行比较,但是这种方式则是将代码写死了

而用另一种方法则是定义一个类型是一个仿函数,它重载的是operator(),是它的对象。因此在比较的时候就可以对它进行调用。 

按照同样的方式,我们也可以对向上调整的代码进行一个仿函数的书写

同时,仿函数operator()中定义的类型也并不是一定是整形,类似double等类型也是可以进行比较

特殊用法(指针):

在上文我们说过,仿函数operator()中定义的类型很多

在有些特定的时候,它会有不同的书写方法,有的时候可能还需要我们自己去写它的仿函数。就拿我们的当初判断日期的函数来讲解

类似上图,如果我们在priority_queue中书写的是指针,也就是在优先级队列中存放的是结点的指针

如果是这样子书写的话可能会导致我们比较大小的结果出现差错

就像上图一样,两次判断大小的结果不一样。 

这也就是类型是指针带来的一系列的问题,在这个地方它默认是类型进行比较,而我们的类似是指针。 

而我们new出来的地址的位置和大小是不确定的,new出来的地址一直在发生变化。 

这里就需要我们自己去写它的仿函数去控制实现它

像这里就控制仿函数去实现比较

在这里我们就能对其进行修改,根据下面你的不同对上面的代码进行修改,因为下面的类型是指针所以在operator中的类型我们也修改为Date*,这样下去二者比较的就不是指针而是它们的对象

就不会发生地址改变影响结果的情况了

与此同时下面的类的参数也要进行修改。

结尾: 

到这里我们的优先级队列和仿函数就了解了不少了,仿函数方面的内容可以不是讲得那么的全面,最后希望这篇博客能带来些许帮助吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
优先队列(priority_queue)是C++中的一种数据结构,它可以按照元素的优先级进行插入和删除操作。在C++中,优先队列默认使用vector作为底层存储数据的容器,并使用堆算法将vector中的元素构造成堆的结构。优先队列最常用的操作包括push、pop和top。 要遍历优先队列中的元素,可以使用while循环和empty函数来实现。首先,可以使用top函数获取优先队列中的最大(或最小)元素,并将其打印出来。然后,使用pop函数将该元素从优先队列中删除。重复这个过程,直到优先队列为空,即使用empty函数判断优先队列是否为空。在每次循环中,可以将获取的元素打印出来。 下面是一个示例代码来遍历优先队列: ```c++ #include <iostream> #include <queue> using namespace std; int main() { priority_queue<int> pq; pq.push(3); pq.push(8); pq.push(2); pq.push(6); pq.push(9); while (!pq.empty()) { cout << pq.top() << " "; pq.pop(); } cout << endl; return 0; } ``` 以上代码创建了一个优先队列pq,并依次插入了5个元素。然后使用while循环和empty函数遍历优先队列,每次输出堆顶元素,并将其从优先队列中删除,直到优先队列为空。最终输出的结果是按照从大到小的顺序输出了优先队列中的所有元素。 因此,c++优先队列的遍历可以通过循环的方式来实现。在每次循环中,使用top函数获取堆顶元素并打印,然后使用pop函数将其删除。重复这个过程直到优先队列为空。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [C++中算法(优先队列、遍历算法、查找算法、排序算法)](https://blog.csdn.net/qq_41915323/article/details/94664541)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [C++——优先级队列](https://blog.csdn.net/qq_55712347/article/details/128874870)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值