优先队列(priority_queue)的妙用(以力扣周赛“K 次增加后的最大乘积”为例)

2023-05-16

1.priority_queue的用法

优先队列在实际的动态更新过程中帮我们找到最大或者最小的元素,priority_queue的使用需要先包含头文件<queue>,即#include <queue>

priority_queue的本质是队列,拥有队列的一切操作,区别是在队列的基础上增添了内部的排序操作,这为我们在循环中动态更新最小值或者最大值提供了极大的便利,优先队列的操作如下:

  • top() 访问优先队列首元素
  • empty() 优先队列是否为空
  • size() 返回优先队列元素个数
  • push() 插入元素到优先队列队尾
  • emplace() 原地构造一个元素并插入优先队列
  • pop() 弹出优先队列队首元素
  • swap() 交换元素

在使用优先队列时,我们需要先进行定义,根据定义方式的不同,优先队列可以定义为大顶堆和小顶堆,所谓大顶堆的意思就是优先队列的队首元素是整个队列中最大的,其后元素按照从大到小进行排列,小顶堆则相反。

根据实际过程需要,我们希望队首元素为最大元素或者最小元素,那么如何来定义大顶堆或者小顶堆呢?

首先看优先队列的数据类型priority_queue<Type, Container, Functional>,其中Type 为数据类型,Container 为容器类型,Functional 为比较的方式,一般用反函数,需要说明的是Container必须是用数组实现的容器,比如vector,deque等,但不能用 list,默认情况下使用vector,使用基本数据类型时,只需要传入数据类型,默认为大顶堆,下面是int类型的使用举例:

// 优先队列的大顶堆定义方式
priority_queue<int> pq; //默认情况下使用为大顶堆
priority_queue<int,vector<int>,less<int>> pq; // 定义为大顶堆
// 优先队列的小顶堆定义方式
priority_queue<int,vector<int>,greater<int>> pq; // 定义为小顶堆

当需要用自定义的数据类型时可以通过重载操作符的方式实现优先队列的自定义排序:

struct Status{
    int val;
    bool operator < (const Status& a)const{
        return a.val < val;
    } 
};

priority_queue<Status> pq; // 小顶堆

2.例题分析与应用

下面为力扣第288场周赛第三题,前面两题相对来说能够直接用暴力解法解开,后面一题对现在的我来说又比较难(哭了°(°ˊДˋ°) ° ),正好这一道题不算难,也符合今天优先队列的主题,所以拿出来讲一讲。

 分析题目的意思和示例,其实我们能够很好的推出来,在有限的k次+1操作内,要使得最后得到的乘积最大,我们要做的肯定是,每一次操作后的数组中取出最小的元素x_min,+1操作后放回数组中去,为什么呢?不妨反过来想一下,这里记总的元素乘积为M,如果我们拿的不是最小的元素,记该元素为x,+1操作后最后的乘积多个M/x,而如果拿的是最小的元素x_min,+1操作后最后的乘积会多个M/x_min,显然M/x_min > M/x,所以本题的唯一难点是如何在k次+1操作这样动态的过程中以最快的时间取出最小的元素+1后放回数组中。

如果不计时间的话,照这个逻辑,很通顺的能够想到先对数组进行排序,然后取出第一个元素,+1后不断与后面元素比较,直到找到比它大的元素,然后放在它前面,进入下一次循环,但是这种实现方式时间复杂度过高,这时我们就该考虑用到优先队列的方式。

使用优先队列我们可以采用小顶堆的方式,先将数据元素放入优先队列中,然后进行k次循环操作,不断用top()拿出最小的元素,+1操作后利用push()放回优先队列,然后调用pop()删除队首元素。k次循环结束后,我们将队列中的每个元素乘积取余即可。

class Solution {
    const int long_num = (pow(10,9)+7);//避免乘积过大取余用
public:
    int maximumProduct(vector<int>& nums, int k) {
        priority_queue<int,vector<int>,greater<int>> pq;// 利用greater仿函数建立小顶堆
        for(auto num : nums) pq.push(num); // 往小顶堆放入元素

        int index = 0; // 指示用,表示进行k次操作
        while(index < k){
            int temp_num = pq.top(); // 取优先队列中的最小元素
            pq.pop(); // 删除队列首元素,也就是原先队列中最小的元素
            ++temp_num; // 最小元素加1
            ++index; // 操作数加1
            pq.push(temp_num); // 加增加后的元素重新放入优先队列中进行排序
        }

        long long ans = 1; // 乘积初始值
        while(!pq.empty()){
            int temp_multi = pq.top(); // 取优先队列中的队首元素
            pq.pop(); // 删除队列首元素
            ans *= temp_multi; // 计算乘积结果
            ans %= long_num; // 取余,避免乘积值过大
        }
        return ans; // 返回计算结果
    }
};

当然,也可以用默认大顶堆的方式来实现,只需要在放入元素时取其负数放入,k次循环中+1操作变为-1操作即可。

class Solution {
    const int long_num = (pow(10,9)+7);//避免乘积过大取余用
public:
    int maximumProduct(vector<int>& nums, int k) {
        priority_queue<int> pq;// 默认建立大顶堆
        for(auto num : nums) pq.push(-num); // 往大顶堆放入元素

        int index = 0; // 指示用,表示进行k次操作
        while(index < k){
            int temp_num = pq.top(); // 取优先队列中的最大元素
            pq.pop(); // 删除队列首元素,也就是原先队列中最大的元素
            --temp_num; // 最小元素减1
            ++index; // 操作数加1
            pq.push(temp_num); // 加增加后的元素重新放入优先队列中进行排序
        }

        long long ans = 1; // 乘积初始值
        while(!pq.empty()){
            int temp_multi = pq.top(); // 取优先队列中的队首元素
            pq.pop(); // 删除队列首元素
            ans *= (-temp_multi); // 计算乘积结果
            ans %= long_num; // 取余,避免乘积值过大
        }
        return ans; // 返回计算结果
    }
};

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

优先队列(priority_queue)的妙用(以力扣周赛“K 次增加后的最大乘积”为例) 的相关文章

  • stack,foreach,顺序错误?

    当使用Java的for每个语法 Stack不对输出元素使用 LIFO 排序 考虑以下代码 import java util Queue import java util Stack import java util LinkedList p
  • 在 MySQL 中创建链表或类似队列?

    我有一个需要按特定顺序显示的项目表 但该顺序可以更改 可以在开头 结尾或中间添加项目 并且可以重新排列项目 如何设置表来跟踪该顺序 以便易于修改 但也可以通过单个查询按顺序获取列表 例如 我可以有一个 NEXT ID 列来执行链接列表样式
  • Swift:为蓝牙中央管理器选择队列

    我正在开发一个应用程序 该应用程序将通过 BLE 与智能设备连接并与其通信 问题是 在哪个队列中处理蓝牙事件的最佳实践是 我读过很多教程 在所有教程中我发现了这一点 centralManager CBCentralManager deleg
  • 具有 async/await 风格函数的 async.queue

    我正在尝试创建一个函数 该函数从对象数组构建队列 然后通过调用多个函数来处理每个对象 处理函数是异步函数 在需要排队之前 我使用异步 等待模式实现了这些函数 我认为这是必要的 因为每个都依赖于前一个的输出 并且我不想有大量嵌套的 Promi
  • Python 中内置最大堆 API

    默认 heapq 是最小队列实现 想知道是否有最大队列的选项 谢谢 我尝试使用 heapify max 作为最大堆的解决方案 但如何动态处理推送 弹出元素 看来 heapify max 只能在初始化时使用 import heapq def
  • 当同步/异步与串行/并发队列混合时,调度程序如何工作?

    在 Grand Central Dispatch 中 调度程序如何处理不同的队列 serial and concurrent 当使用dispatch sync函数和dispatch async功能 首先我们需要两种类型queue one s
  • Python 中的线程队列挂起

    我正在尝试通过队列使解析器成为多线程 它似乎有效 但我的队列挂起 如果有人能告诉我如何解决这个问题 我将不胜感激 因为我很少编写多线程代码 此代码从 Q 中读取 from silk import import json import dat
  • 通过 C++ 互操作或其他方式实现 C# 第一类延续?

    我们有一个非常高性能的多任务 近乎实时的 C 应用程序 这一性能主要是通过使用自制的调度程序在内部实施协作多任务来实现的 这通常称为微线程 在这个系统中 所有任务都通过队列与其他任务通信 我们遇到的具体问题似乎只能通过 C 不支持的第一类延
  • Node.js 公牛队列中的作业陷入“等待”状态

    我有一堆工作在公牛队列中 其中一个被卡住了 1 个多小时 通常需要大约 2 分钟才能运行 但没有失败 我无法使用我使用的 bull arena UI 将作业从活动状态中删除 因此我删除了 Redis 中活动作业的密钥 这消除了卡住的活动作业
  • 有没有更好的方法来实现队列的删除方法?

    首先 请承认我确实想要一个功能Queue
  • SQL Server 进程队列竞争条件

    我有一个订单队列 多个订单处理器通过存储过程访问该队列 每个处理器都会传递一个唯一的 ID 该 ID 用于锁定接下来的 20 个订单以供自己使用 然后 存储过程将这些记录返回给订单处理器以进行操作 有些情况下多个处理器能够检索相同的 Ord
  • 如何在运行时创建 celery 队列,以便工作人员拾取发送到该队列的任务?

    我正在使用 django 1 4 celery 3 0 rabbitmq 为了描述该问题 我的系统中有许多内容网络 并且我需要一个队列来处理与每个网络相关的任务 然而 内容是在系统运行时动态创建的 因此我需要动态创建队列并让现有工作人员开始
  • 使用 Celery(RabbitMQ、Django)检索队列长度

    我在 django 项目中使用 Celery 我的代理是 RabbitMQ 我想检索队列的长度 我浏览了 Celery 的代码 但没有找到执行此操作的工具 我在 stackoverflow 上发现了这个问题 从客户端检查 RabbitMQ
  • NodeJS 推送队列,由 Laravel Worker 消耗

    我正在尝试使用节点应用程序发送到 SQS 的消息 因此 推送 操作由服务器 A 上的 Node App 执行 监听 操作由服务器 B 上的 Laravel App 执行 我的问题 我不知道如何格式化要使用的有效负载php artisan q
  • 异步运行 PHP 任务

    我正在开发一个较大的 Web 应用程序 后端主要是 PHP 代码中有几个地方我需要完成某些任务 但我不想让用户等待结果 例如 当创建一个新帐户时 我需要向他们发送一封欢迎电子邮件 但是 当他们点击 完成注册 按钮时 我不想让他们等到电子邮件
  • 原子地从 ConcurrentQueue 中获取所有内容

    我有多个线程生成项目并将它们粘贴在一个公共的ConcurrentQueue private ConcurrentQueue
  • 使用 Java 中的映射实现的队列数据结构,大小限制为 5

    我有带有一些记录的地图 我想将该映射限制为仅 5 个元素 并且每当添加新元素时 应删除第一个元素 并应在映射的最后位置添加新元素 类似于 FIFO 的东西 任何人都可以建议我使用一个数据结构或解决方案本身 E g Map
  • Node Js:Redis 作业在完成其任务后未完成

    希望你们做得很好 我在我的 Nodejs 项目中实现了 BullMQ Bull 的下一个主要版本 来安排发送电子邮件的作业 例如 发送忘记密码请求的电子邮件 所以 我编写了如下所示的代码 用户服务 await resetPasswordJo
  • C# 创建函数队列

    我写了一个名为 QueueManager 的类 class QueueManager Queue functionsQueue public bool IsEmpty get if functionsQueue Count 0 return
  • 洪水填充优化:尝试使用队列

    我正在尝试创建一种填充方法 该方法采用用户指定的初始坐标 检查字符 然后根据需要更改它 这样做之后 它会检查相邻的方块并重复该过程 经过一番研究 我遇到了洪水填充算法并尝试了该算法 它可以工作 但无法满足我对 250 x 250 个字符的数

随机推荐