【C++】priority_queue使用和模拟实现——仿函数

news2025/1/18 18:12:21

文章目录

  • 1. priority_queue的使用
    • 1.priority_queue的介绍
    • 2.priority_queue的结构
    • 3. 主要接口
    • 4. 使用示例
  • 2. 仿函数
    • 1. 仿函数的概念
    • 2.尝试实现仿函数
  • 3.priority_queue的模拟实现
    • 1.priority_queue的结构
    • 2. 接口实现
      • 1.向下调整算法
      • 2. 向上调整算法
      • 3.构造函数
      • 4.修改数据
      • 5.获取数据

首先,在这里还是推荐一下我正在用的一个C++的查询文档网站,这里是关于 priority_queue的使用文档。有什么本文中没有讲清楚的东西,可以去参考这个网站的内容。

1. priority_queue的使用

1.priority_queue的介绍

priority_queue(优先级队列),是包含在<queue>头文件下的一个容器适配器。下面是cplusplus网站对priority_queue的介绍。

image-20230427143323115

可以看到,priority_queue是一个容器适配器默认的容器是vector。他和队列在使用上是类似的,只是出队列的规则不同,queue是按照入队列的顺序出队列,priority_queue是按照优先级出队列,这里的优先级是按照类模板的第三个参数决定的,这里的第三个参数是一个仿函数,关于仿函数的概念将会在下文中详细讲解。

2.priority_queue的结构

priority_queue在底层的逻辑上,是一个堆,每次pop的都是堆定的数据,关于堆的讲解,可以去看一下博主之前写的【数据结构】树与二叉树,里面对堆这个数据结构讲解的还是比较清楚的。

3. 主要接口

priority_queue是一个容器适配器,所以接口基本上都差不多,我们看一下文档:

image-20230427145626883

可以看到,接口和stack、queue基本相同,这里给出几个重点的函数接口:

函数接口接口说明
priority_queue()构造一个空的优先级队列
priority_queue(first, last)按照迭代器区间构造一个优先级队列
empty()判断优先级队列是否为空,返回bool类型
top()返回堆顶元素(被cosnt修饰)
push(x)插入一个数据
pop()删除堆顶数据

4. 使用示例

了解了上述的一些接口之后,总是要实践一下的,接下来我们使用以下这个结构,来测试一下:

//这里放一下测试代码,读者可以自行拷贝下去测试
void Test1()
{
	priority_queue<int> heap;
	heap.push(5);
	heap.push(3);
	heap.push(7);
	heap.push(10);
	heap.push(1);
	heap.push(9);
	while (!heap.empty())
	{
		cout << heap.top() << " ";
		heap.pop();
	}
	cout << endl;
}

image-20230427150918002

可以看到,默认情况pop出来的是当前堆内的最大值,所以可知默认的堆是大堆

❓那么如果想让堆变成小堆需要怎么做呢?
✅这里就要注意到,在文章的开头,我们讲到的类模板的第三个参数。可以看到默认传的参数是less,这就是表示默认建立大堆,如果想建小堆的话需要传的仿函数就是greater
image-20230427152520257

接下来,就用刚学的priority_queue做一道OJ题吧:215. 数组中的第K个最大元素 - 力扣(LeetCode).

我是题解,点我

2. 仿函数

1. 仿函数的概念

在上文中,我们看到priority_queue类模板中,有三个模板参数,其中第三个就是仿函数

仿函数到底是什么呢?

仿函数(functors),也叫函数对象(function objects),是STL六大组件中的一部分,这里我们没办法一次性讲完它,所以就基于priority_queue的实现稍微介绍一下。

实际上,就实现意义而言,函数对象这个名字更加贴切:一种具有函数特质的对象。但是,仿函数似乎能更加符合的描述他的行为。所以这里我们就采用仿函数这种叫法。

在学习STL之前我们就已经了解了泛型编程的概念,C++引入了模板让我们的编程能够随意的控制数据类型,现在引入了仿函数的概念,让我们能够控制逻辑。

那么现在让我们见见仿函数:

image-20230428000620364

image-20230428000825943

这两个就是我们在priority_queue的参数列表中可能用到的仿函数。

image-20230428002052512

可以看到,在使用的时候,实际上类似一个函数的调用,因此被称为仿函数

2.尝试实现仿函数

仿函数的本质就是一个运算符重载operator(),重载的是函数调用的运算符。由于仿函数本身也就是一个类模板,所以我们的实现如下

template<class T>
class less
{
    public:
    bool operator()(const T& x, const T& y)//less和greater需要实现的就是一个比较,所以这里的返回值是bool类型
    {
        return x < y;
    }
};
template<class T>
class greater
{
    public:
    bool operator()(const T& x, const T& y)
    {
        return x > y;
    }
};

这样就算是实现了less和greater的仿函数。

这里只是稍微提了一下仿函数的概念,仿函数毕竟是STL六大组件之一,其中包含的东西还有很多,我们对仿函数的学习还在路上

3.priority_queue的模拟实现

有了上述只是的铺垫,现在我们已经有了模拟实现priority_queue的能力,那么just do it。

1.priority_queue的结构

首先,对于函数模板的设计,我们和库里面对其,给了三个参数,分别表示参数存入容器的参数类型,容器类型和仿函数,其中默认的仿函数是less,建大堆。

template<class T, class Container = std::vector<T>, class Compare = less<T>>
class priority_queue
{
public:
    //...
private:
    Container _con;
}; 

2. 接口实现

按照我们之前在【数据结构】树与二叉树实现堆的经验,一定需要实现的两个接口是向上调整和向下调整,这也是整个堆的核心接口,所以我们就先着重实现这两个功能性接口,这两个接口实现完成之后,其他的结构性接口都很容易实现啦。

1.向下调整算法

image-20230428004532849

这里,我们以小堆为例(在这里偷个懒:小堆的图有以前画过的),我们需要做的事情就是找到两个孩子中较小的,然后与父节点比较大小,如果父节点大于子节点就执行交换,然后原来的子节点成为新的父节点,再次进行上述步骤。直到符合堆的结构

void adjust_down(size_t parent)
{
    Compare cmp;//实例化仿函数
    int minchild = 2 * parent + 1;
    while (minchild < _con.size())
    {
        if (minchild + 1 < _cin.size() && cmp(_con[minchild], con[minchild + 1]))//使用仿函数找到符合条件的子节点
        {
            minchild++;
        }
        if (cmp(_con[parent], _con[minchild]))//判断是否满足堆结构
        {
            std::swap(_con[parent], _con[minchild]);
            //父子节点迭代
            parent = minchild;
            minchild = 2 * parent + 1;
        }
    }
}

2. 向上调整算法

image-20230428010029365

这里向上调整的操作,从指定的孩子节点位置开始,和父节点比较,判断是否满足堆结构,如果不满足,就交换父子节点,然后原来的父节点编程子节点,再次进行上述操作,直到满足堆结构为止。

void adjust_up(size_t child)
{
    Compare cmp;//实例化仿函数
    size_t parent = (child - 1) / 2;
    while (child > 0)
    {
        if (cmp(_con[parent], _con[child]))
        {
            std::swap(_con[parent], _con[child]);
            //父子迭代
            child = parent;
            parent = (child - 1) / 2;
        }
    }
}

3.构造函数

构造函数分为无参的构造和迭代器区间构造

//无参的构造函数
priority_queue() {}
//迭代器区间构造
template<class InputIterator>
priority_queue(InputIterator first, InputIterator last)
    _con(first, last)
{
    //从最后一个父节点的位置开始向下调整建堆,效率最高
    for (int i = (_con.size() - 1 - 1) / 2; i <= 0; --i)
    {
        adjust_down(i);
    }
}

4.修改数据

priority_queue的数据修改只有push和pop两种情况

push数据就直接尾插,然后向上调整堆,直到满足堆的结构即可,pop数据就交换堆顶数据和最后一个数据,然后容器pop_back,然后向下调整直到剩余数据满足堆结构即可

void push(const T& val)
{
    _con.push_back(val);//尾插数据
    adjust_up(_con.size() - 1);//向上调整堆结构
}
void pop()
{
    swap(_con[0], _con[_con.size() - 1]);//交换堆顶和堆内最后一个元素
    _con.pop_back;//容器尾删
    adjust_down(0);//向下调整堆结构
}

5.获取数据

剩下的接口就直接复用容器提供的接口即可

bool empty() const
{
    return _con.empty();
}
size_t size() const
{
    return _con.size();
}
const T& top() const
{
    return _con[0];
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/470352.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习 -Statsmodels

机器学习记录 Statsmodels 用于探索数据, 估计模型, 并运行统计检验. conda install -y statsmodels线性回归 import numpy as np import pandas as pd import matplotlib.pyplot as plt import statsmodels.api as sm import statsmodels.datasets.utils as du import sea…

数据结构【二】:霍夫曼编码

霍夫曼编码&#xff08;Huffman Coding&#xff09;是可变长编码&#xff08;VLC&#xff09;的一种。本质上使用变长编码表对源符号进行编码&#xff0c;通过评估源符号出现概率的方法进行分类&#xff0c;将出现几率较高的源字符使用较短的编码&#xff0c;出现几率较低的源字…

Hive优化补充

目录 一、表设计优化 1.通过设计分区表&#xff0c;增加动态分区&#xff0c;查询时避免全表扫描 2.设计分桶表&#xff1a;适用于大表join大表的情况 最后&#xff0c;两张大表进行join转为两张分桶表进行join&#xff1a; 二、文件存储 1.文件格式-概述 2.文件格式——…

学系统集成项目管理工程师(中项)系列13b_人力资源管理(下)

1. 项目团队建设 1.1. 塔克曼(Tuckman)阶梯理论 1.2. 理论基础 1.2.1. 激励理论 1.2.1.1. 马斯洛需要层次理论 1.2.1.1.1. 生理需要 1.2.1.1.2. 安全需要 1.2.1.1.3. 社会交往的需要 1.2.1.1.4. 自尊的需要 1.2.1.1.5. 自我实现的需要 1.2.1.2. 赫茨伯格的双因素理论…

Leetcode力扣秋招刷题路-0802

从0开始的秋招刷题路&#xff0c;记录下所刷每道题的题解&#xff0c;帮助自己回顾总结 802. 找到最终的安全状态 有一个有 n 个节点的有向图&#xff0c;节点按 0 到 n - 1 编号。图由一个 索引从 0 开始 的 2D 整数数组 graph表示&#xff0c; graph[i]是与节点 i 相邻的节…

Git HEAD及detached head

背景&#xff1a;最近在使用git checkout重置HEAD指向&#xff0c;偶尔会出现Detached HEAD提示&#xff0c;于是想探究一下具体的原理及过程&#xff0c;遂写下了这篇文章。一般checkout用于切换分支和检出历史的某个节点&#xff0c;或恢复工作区的文件&#xff0c;这三个功能…

OpenHarmony JS Demo开发讲解

项目结构 打开entry→src→main→js&#xff0c;工程的开发目录如图所示 其中&#xff0c; i18n文件夹&#xff1a;用于存放配置不同语言场景的资源&#xff0c;比如应用文本词条&#xff0c;图片路径等资源。en-US.json文件定义了在英文模式下页面显示的变量内容&#xff0c…

Java 泛型为什么设计成是可以擦除的

Java 泛型是 Java 5 引入的一种类型安全的编程机制&#xff0c;它允许在编译时指定泛型类型参数&#xff0c;从而提高代码的类型安全性和可读性。然而&#xff0c;Java 泛型的实现方式是通过类型擦除来实现的&#xff0c;这也引发了一些争议。本文将介绍 Java 泛型为什么设计成…

2023年某科技公司前端开发初级岗的面试笔试真题(含选择题答案、问答题解析、机试题源码)

📚关于该专栏: 该专栏的发布内容是前端面试中笔试部分真题、答卷类、机试等等的题目,题目类型包括逻辑题、算法题、选择题、问答题等等,除了内容的分享,还有解析和答案。真实来自某些互联网公司,坐标广东广州。 🔥🔥🔥 持 续 更 新 🔥🔥🔥 😉专栏博主: 黛…

HCIP-7.1交换机ARP、VLAN之间的三层通信技术学习

交换机ARP、VLAN之间的三层通信技术学习 1、ARP1.1、 地址解析过程1.2、ARP报文格式1.3、ARP表项1.4、免费ARP1.5、 VLAN间ARP代理1.5.1、解决同网段&#xff0c;不同广播域内主机互通问题&#xff1b;1.5.2、解决同网段&#xff0c;不同VLAN之间主机互通问题。1.5.3、解决同网…

Ignore insecure directories and continue [y] or abort compinit [n]?

问题&#xff1a; 在Mac终端中使用Zsh作为默认shell时&#xff0c;有时会弹出以下提示信息&#xff1a; Ignore insecure directories and continue [y] or abort compinit [n]? 这个提示出现的原因是因为Zsh在加载时会检查所有的目录是否安全&#xff0c;并拒绝加载不安全的…

【LeetCode: 62. 不同路径 | 暴力递归=>记忆化搜索=>动态规划 】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

卡尔曼滤波器简介——概述

关于卡尔曼滤波器 大多数现代系统都有许多传感器&#xff0c;可以根据一系列测量来估计隐藏&#xff08;未知&#xff09;状态。例如&#xff0c;GPS接收器提供位置和速度估计&#xff0c;其中位置和速度是隐藏状态&#xff0c;卫星信号到达的差分时间是测量值。 跟踪和控制系统…

ChatGPT的进化版?AutoGPT怎么用

AutoGPT是什么 首选给大家介绍&#xff0c;ChatGPT与AutoGPT的区别 目前AutoGPT被称为最接近AGI的人工智能&#xff0c;它是ChatGPT的进化版&#xff1f; “ChatGPT” 只能提供2021年9月之前的信息&#xff0c;所以你问它告诉我今天的天气&#xff0c;它回答不了 “AutoGPT” …

AutoGPT不靠谱,微软推出升级版!可编辑自主规划过程

夕小瑶科技说 原创 作者 | iven 火遍全网的AutoGPT[1]在Github收藏量突破十万。这种自我规划、自我执行的智能体首次关注人工智能模型内部的自我调整与优化。 但是有不少网友发现&#xff0c;AutoGPT的表现不稳定&#xff0c;死循环是最常见的现象。此外&#xff0c;AutoGPT执…

输入指令为±10V或4~20mA型伺服阀控制器

工作电压 19~35 VDC&#xff08;常规24VDC&#xff09; 最大功率消耗 &#xff1c;25VA 空载电流 ≤100mA&#xff08;24V&#xff09; 差分信号输入 0~10 V&#xff0c;输入阻抗≥100KΩ 4~20 mA&#xff0c;输入阻抗100Ω &#xff08;出厂前需指定&#xff0c;现场不可…

免费的ERP系统哪个好?这款让管理更高效

阅读本文你将了解&#xff1a;ERP是什么&#xff1f;解决什么问题&#xff1f;ERP选型的参考维度&#xff1f;零代码ERP系统解决哪些场景问题&#xff1f; 题目提到“免费”&#xff0c;其实很难有软件可以真正做到。 商业化市场决定了没有一家厂商可以不落俗套。因而我们要探…

[架构之路-177]-《软考-系统分析师》-17-嵌入式系统分析与设计 -2- 系统分析与设计、低功耗设计

目录 1 7 . 4 嵌 入 式 系 统 开 发 17.4.1 开发平台 1 . 交叉开发环境 2 . 交叉编译环境 17. 2 开发流程 1. 过程模型 2 . 分析与设计方法 17.4.3 软硬件协同设计 1 . 软 硬 件 协 同 设 计 方 法 2 . 协 同 设 计 工 具 17.4.4 系统分析与设计 1 . 需求分析 2 .…

CI/CD: GitLab Runner安装注册配置管理

点击上方蓝字⭐️关注“DevOps云学堂”&#xff0c;接收最新技术实践 今天是「DevOps云学堂」与你共同进步的第 21 天 本文是《GitLabCI实践》教程部分内容 GitLab Runner是一个开源项目&#xff0c;用于运行您的作业并将结果发送回GitLab。它与GitLab CI结合使用&#xff0c;G…

计算机网络学习10(ARP协议详解)

ARP 协议&#xff0c;可以说是在协议栈中属于一个偏底层的、非常重要的、又非常简单的通信协议。 开始阅读这篇文章之前&#xff0c;你可以先看看下面几个问题&#xff1a; ARP 协议在协议栈中的位置&#xff1f; ARP 协议在协议栈中的位置非常重要&#xff0c;在理解了它的工…