优先队列用法详解(c++)

news2024/11/22 10:11:14

一、简介

1、定义

优先队列相比普通队列,元素具有优先级,从原来的先进先出变成了最高级先出。换句话就是,原来队列是按照时间顺序从头到尾排列,现在变成了按照优先级高低从头到尾排列

2、基本使用方式

  • 头文件:#include

  • 定义:priority_queue<Type, Container, Functional>
    注意:
    ①type:优先队列种存储的元素的数据类型(没有限制)
    ②container:必须是基于数组的容器,默认vector,deque也可以,但不能list
    > 因为priority_queue 是基于堆(通常是二叉堆)实现的,而堆是一种完全二叉树,要求以 连续内存存储数据,需要容器支持随机访问,所以list不可以。为什么默认使用vector而不用deque(双端队列)呢?因为对于堆操作,vector比deque更加高效,deque的存储方式是分块连续(多个小连续块组合),虽然支持随机访问,但索引操作稍慢

  • 两种形式:
    ①//升序队列,小顶堆
    priority_queue <int,vector,greater > q;
    ②//降序队列,大顶堆
    priority_queue <int,vector,less >q;

  • 默认类型
    //对于基础类型 默认是大顶堆
    //此时不用传入那三个参数,只用声明数据类型即可
    priority_queue a;

  • 操作:
    具有队列的所有特性,基本操作和队列相同
    a.top 访问队头元素
    b.empty 队列是否为空
    c.size 返回队列内元素个数
    d.push 插入元素到队尾 (并排序)
    e.emplace 原地构造一个元素并插入队列
    f.pop 弹出队头元素
    g.swap 交换内容

二、自定义使用情况

1、用pair做优先队列元素

规则:先比较第一个元素,第一个相等的情况下比较第二个

#include <iostream>
#include <queue>
#include <vector>

using namespace std;

int main() {
    // 定义优先队列,元素类型为 pair<int, int>
    // 优先队列中存储 (优先级, 值),我们想要按优先级排序
    priority_queue<pair<int, int>> pq;

    // 插入一些元素到优先队列中
    pq.push({3, 100}); // 插入元素 (3, 100)
    pq.push({1, 200}); // 插入元素 (1, 200)
    pq.push({4, 50});  // 插入元素 (4, 50)
    pq.push({2, 300}); // 插入元素 (2, 300)

    // 优先队列会自动将元素按第一个值 (优先级) 从大到小排序
    cout << "Elements in priority queue (sorted by priority):" << endl;

    // 遍历优先队列,输出元素
    while (!pq.empty()) {
        // 获取堆顶元素(具有最高优先级的元素)
        pair<int, int> top = pq.top();
        pq.pop(); // 移除堆顶元素

        // 输出元素:优先级 和 值
        cout << "Priority: " << top.first << ", Value: " << top.second << endl;
    }

    return 0;
}

结果:

Elements in priority queue (sorted by priority):
Priority: 4, Value: 50
Priority: 3, Value: 100
Priority: 2, Value: 300
Priority: 1, Value: 200

2、用自定义类型(排序规则)做优先队列元素部分

(1)什么是自定义类型

自定义类型通常是用户定义的结构体 (struct) 或类 (class),用于存储多个相关的数据字段

(2)实现方式1:重载<运算符
①概念

重载 < 运算符是在自定义类型中直接定义比较规则的最直接方法。std::priority_queue 默认使用最大堆(std::less),其排序规则依赖于元素的 < 运算符。因此,重载 < 运算符可以让优先队列理解自定义类型的比较逻辑。

②步骤
  • 定义一个自定义类型(struct 或 class)。
  • 在该类型中重载 < 运算符。
  • 使用优先队列(std::priority_queue)存储该类型的对象,优先队列会自动按照定义的 < 运算符排序。
③重载函数示例
 bool operator<(const Task& other) const {
        return priority < other.priority; // 优先级大的排在堆顶
    }
};

const Task& other:
Task&:参数 other 是要与当前对象 (this) 比较的另一个 Task 对象,使用引用传递以避免拷贝。
const:表示这个参数不会被修改,保证了安全性和一致性(const常和&联合起来用,既能够保证不被修改,也能避免再拷贝一份)

④代码示例
#include <iostream>
#include <queue>
#include <string>
#include <vector>

using namespace std;

// 定义任务结构体
struct Task {
    int priority;       // 优先级
    string name;        // 任务名
    int duration;       // 预计时间

    // 重载 `<` 运算符
    // 这里定义 "优先级高的任务优先"
    bool operator<(const Task& other) const {
        return priority < other.priority; // 优先级大的排在堆顶
    }
};

int main() {
    // 定义优先队列,元素类型为 Task
    priority_queue<Task> pq;

    // 插入任务到优先队列中
    pq.push({3, "Task A", 10}); // 插入任务 (优先级: 3, 名称: Task A, 时长: 10)
    pq.push({5, "Task B", 5});  // 插入任务 (优先级: 5, 名称: Task B, 时长: 5)
    pq.push({1, "Task C", 15}); // 插入任务 (优先级: 1, 名称: Task C, 时长: 15)

    // 输出优先队列内容
    cout << "Tasks in priority queue:" << endl;
    while (!pq.empty()) {
        Task top = pq.top(); // 获取堆顶任务
        pq.pop();            // 移除堆顶任务
        cout << "Priority: " << top.priority
             << ", Name: " << top.name
             << ", Duration: " << top.duration << " mins" << endl;
    }

    return 0;
}

结果:

Tasks in priority queue:
Priority: 5, Name: Task B, Duration: 5 mins
Priority: 3, Name: Task A, Duration: 10 mins
Priority: 1, Name: Task C, Duration: 15 mins

这个原理就是你直接在数据类型里面定义比较函数,当你的数据类型确定了以后,你再使用这个优先队列,它就会按照数据类型里面的优先级

需要注意的是:
关于<的实际含义

bool operator<(const Person& other) const {
    return age > other.age;  // 升序排列:年龄小的优先
}

为什么这里代表的意思是年龄小的优先?
因为优先队列的默认情况是最大堆,也就是较小的元素排在较大的元素的后面,所以这里<的意思就是小的排在后面的意思。那么在重载中,为了让年龄小的元素优先出现在堆顶,我们就需要和原来的反着来,那也就是在return的过程中让大的排在前面。或者也可以直接记住一句口诀:升大降小

3、使用自定义比较器

如果不想修改结构体本身(例如不想重载<运算符),可以通过自定义比较器实现排序

①什么是自定义比较器

自定义比较器是一个函数或函数对象,用于定义两个元素之间的比较规则。

  • 默认规则: STL 容器如 priority_queue 默认使用 大于运算符(<) 来比较元素。
  • 对于最大堆,priority_queue 按降序排列(堆顶为最大值)。
  • 对于 std::sort,按升序排列。
  • 自定义规则: 通过提供一个自定义比较器,可以按照特定逻辑排序元素。例如:
  • 按字段排序(如优先级)。
  • 按自定义规则(如绝对值、特定条件等)。
②通过函数对象(仿函数)

仿函数是一个重载了 () 运算符的类或结构体。它是自定义比较器的最常用方式

③代码示例
#include <iostream>
#include <queue>
#include <string>
using namespace std;

// 定义任务结构体
struct Task {
    int priority;      // 优先级
    string name;       // 任务名称

    Task(int p, string n) : priority(p), name(n) {}
};

// 自定义比较器(仿函数)
//operator() 是仿函数的核心,它重载了 () 运算符
struct CompareTask {
    bool operator()(const Task& t1, const Task& t2) {
        return t1.priority < t2.priority; // 优先级高的任务排在堆顶
    }
};

int main() {
    // 定义优先队列,指定自定义比较器
    priority_queue<Task, vector<Task>, CompareTask> pq;

    // 插入任务
    pq.push(Task(3, "Task A")); // 优先级 3
    pq.push(Task(5, "Task B")); // 优先级 5
    pq.push(Task(1, "Task C")); // 优先级 1

    // 输出优先队列中的内容
    cout << "Tasks in priority queue:" << endl;
    while (!pq.empty()) {
        Task top = pq.top(); // 获取堆顶任务
        pq.pop();
        cout << "Priority: " << top.priority
             << ", Name: " << top.name << endl;
    }

    return 0;
}

结果:

Tasks in priority queue:
Priority: 5, Name: Task B
Priority: 3, Name: Task A
Priority: 1, Name: Task C

这种情况更适合:比如说你不想直接在数据类型里面定义比较类型,就可以用这种自定义比较器的形式,这种方式的核心就是在一个结构体中定义一种方法,然后将函数的实现改为这种结构体,注意是结构体!

参考文章

1、https://www.cnblogs.com/huashanqingzhu/p/11040390.html

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

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

相关文章

django基于django的民族服饰数据分析系统的设计与实现

摘 要 随着网络科技的发展&#xff0c;利用大数据分析对民族服饰进行管理已势在必行&#xff1b;该平台将帮助企业更好地理解服饰市场的趋势&#xff0c;优化服装款式&#xff0c;提高服装的质量。 本文讲述了基于python语言开发&#xff0c;后台数据库选择MySQL进行数据的存储…

如何使用GPT API 自定义 自己的 RAG

要使用 GPT 的 API 实现自己的 RAG&#xff08;Retrieval-Augmented Generation&#xff09; 系统&#xff0c;可以结合检索工具和 GPT 模型&#xff0c;将外部知识库中的信息与生成模型结合起来&#xff0c;完成准确、高效的任务。以下是具体步骤和实现方法&#xff1a; 系统架…

对subprocess启动的子进程使用VSCode python debugger

文章目录 1 情况概要&#xff08;和文件结构&#xff09;2 具体设置和启动步骤2.1 具体配置Step 1 针对attach debugger到子进程Step 2 针对子进程的暂停(可选) Step 3 判断哪个进程id是需要的子进程 2.2 启动步骤和过程 3 其他问题解决3.13.2 ptrace: Operation not permitted…

cocos creator 3.8 一些简单的操作技巧,材质的创建 1

这是一个飞机的3D模型与贴图 导入到cocos中&#xff0c;法线模型文件中已经包含了mesh、material、prefab&#xff0c;也就是模型、材质与预制。界面上创建一个空节点Plane&#xff0c;将模型直接拖入到Plane下。新建材质如图下 Effect属性选择builtin-unlit&#xff0c;不需…

基于web的音乐网站(Java+SpringBoot+Mysql)

目录 1系统概述 1.1 研究背景 1.2研究目的 1.3系统设计思想 2相关技术 2.1 MYSQL数据库 2.2 B/S结构 2.3 Spring Boot框架简介 3系统分析 3.1可行性分析 3.1.1技术可行性 3.1.2经济可行性 3.1.3操作可行性 3.2系统性能分析 3.2.1 系统安全性 3.2.2 数据完整性 …

Web中间件漏洞总结——IIS篇

0x01 前言 渗透过程中会遇到各种中间件&#xff0c;某些中间件版本存在远程执行、任意文件上传等漏洞。本文对IIS相关漏洞进行整理&#xff0c;方便我们在渗透过程中快速查阅IIS漏洞。文章粗略浅显&#xff0c;适合刚入行的新手观看。 0x02 目录 IIS6.0 PUT漏洞IIS6.0 远程代…

关于中断向量表中没有EXTIx_IRQHandler的问题

如果你在中断向量表查找中断向量服务函数时&#xff0c;没有查找到EXTI7_IRQHandler等&#xff0c;是因为中断向量中根本就没有这个函数。 STM32 的中断向量表通常由启动文件&#xff08;如 startup_stm32f1xx.s&#xff09;定义。在该文件中&#xff0c;所有的中断服务例程&a…

idea启动服务报错Application run failed

现象是这样&#xff0c;在宝兰德部署报错&#xff1a; NoClassDefFoundError: org/apache/tomcat/util/codec/binary/Base64 本地启动报错&#xff1a;Application run failed:Failed to parse configuration class [***.WebApplication]; nested exception is java.lang.Illeg…

Easyexcel(4-模板文件)

相关文章链接 Easyexcel&#xff08;1-注解使用&#xff09;Easyexcel&#xff08;2-文件读取&#xff09;Easyexcel&#xff08;3-文件导出&#xff09;Easyexcel&#xff08;4-模板文件&#xff09; 文件导出 获取 resources 目录下的文件&#xff0c;使用 withTemplate 获…

神经网络问题之:梯度不稳定

梯度不稳定是深度学习中&#xff0c;特别是在训练深度神经网络时常见的一个问题&#xff0c;其本质涉及多个方面。 一、根本原因 梯度不稳定问题的根本原因在于深度神经网络的结构和训练过程中的一些固有特性。随着网络层数的增加&#xff0c;梯度在反向传播过程中会逐层累积变…

动态规划子数组系列一>等差数列划分

题目&#xff1a; 解析&#xff1a; 代码&#xff1a; public int numberOfArithmeticSlices(int[] nums) {int n nums.length;int[] dp new int[n];int ret 0;for(int i 2; i < n; i){dp[i] nums[i] - nums[i-1] nums[i-1] - nums[i-2] ? dp[i-1]1 : 0;ret dp[i…

RedHat系统配置静态IP

1、执行nmtui命令进入字符配置界面如下图所示 2、选择编辑连接进入 3、选择编辑进入后&#xff0c;将IPv4设置为手动模式后&#xff0c;选择显示后进行ip地址、网关、DNS的配置&#xff0c;配置完成后选择确定退出编辑 4、进入主界面后选择启用连接进入后&#xff0c;选择启用&…

batchnorm与layernorn的区别

1 原理 简单总结&#xff1a; batchnorn 和layernorm是在不同维度上对特征进行归一化处理。 batchnorm在batch这一维度上&#xff0c; 对一个batch内部所有样本&#xff0c; 在同一个特征通道上进行归一化。 举个例子&#xff0c; 假设输入的特征图尺寸为16x224x224x256&…

【Redis】持久化机制RDB与AOF

一、RDB RDB模式是就是将内存中的数据存储到磁盘中&#xff0c;等到连接断开的时候会进行持久化操作。但是如果服务器宕机&#xff0c;会导致这个持久化机制不会执行&#xff0c;但是内存中的文件会直接丢失。所以可以设置一个触发机制&#xff0c;save 60 1000 就是代表60秒 执…

JSON,事件绑定

文章目录 JSON事件绑定输入框input和div的内容返回获取dom元素数组还是单个对象for循环为什么要写const那一行&#xff0c;直接写 hobbys[index].checked true;可以吗const不是常量吗&#xff0c;为什么用const声明的element的属性值可以改变&#xff1f; 黑马学习笔记 JSON 定…

Kotlin Multiplatform 未来将采用基于 JetBrains Fleet 定制的独立 IDE

近期 Jetbrains 可以说是动作不断&#xff0c;我们刚介绍了 IntelliJ IDEA 2024.3 K2 模式发布了稳定版支持 &#xff0c;而在官方最近刚调整过的 Kotlin Multiplatform Roadmap 优先关键事项里&#xff0c;可以看到其中就包含了「独立的 Kotlin Multiplatform IDE&#xff0c;…

并行优化策略

并行优化策略汇总 并行优化策略 数据并行&#xff08;DP&#xff09; 将数据集分散到m个设备中&#xff0c;进行训练。得到训练数据后在进行allreduce操作。确保每个worker都有相同模型参数。 整体流程如下 若干块计算GPU&#xff0c;如图中GPU0~GPU2&#xff1b;1块梯度收集…

解决 Android 单元测试 No tests found for given includes:

问题 报错&#xff1a; Execution failed for task :testDebugUnitTest. > No tests found for given includes: 解决方案 1、一开始以为是没有给测试类加public修饰 2、然后替换 Test 注解的包可以解决&#xff0c;将 org.junit.jupiter.api.Test 修改为 org.junit.Tes…

知识见闻 - 数学: 均方根 Root Mean Square

What is Root Mean Square (RMS)? 在统计学上&#xff0c;均方根&#xff08;RMS&#xff09;是均方的平方根&#xff0c;而均方是一组数值的平方的算术平均数。均方根也称为二次均值&#xff0c;是指数为 2 的广义均值的一种特例。均方根也被定义为基于一个周期内瞬时值的平方…

寻找用户推荐人(考点:ifnull)【SQL+Pandas】

今天尝试刷一下力扣的sql面试题&#xff0c;这个写法我也是第一次见 题目是 我们需要在这个表中查出referee_id&#xff01;2的 正确写法是 select name from customer where ifnull(referee_id,0) ! 2 -- 不等于还可以这么写&#xff1a;<>