优先队列 priority_queue详解

news2025/3/25 8:15:30

 说到,priority_queue优先队列。必须先要了解啥是与运算符重载(我在下方有解释)。

否则只知皮毛,极易忘记==寸步难行。

但在开头,还是简单的说下怎么用

首先,你需要调用

#include <queue>

在main函数中,声明格式为:priority_queue<结构类型> 队列名;

priority_queue <int> i;
priority_queue <double> d;

常用操作

priority_queue<int> p;
p.size(); // 获取长度
p.empty(); // 是否为空
p.push(val); // 插入
p.pop(); // 删除首个元素
p.top(); // 获取头部元素

以下是完整应用:

priority_queue隶属于<queue>

而queue又是容器适配器。

故,priority_queue也是容器适配器

容器适配器 代表这个函数底层,是可插拔的,能用(vector、deque实现)。
但要注意list不能作为底层容器,因为list不支持随机访问 (如operator[])
简而言之,priority_queue就像汽车,而内部的发动机,有原装的。如果你不满意,也可以换成其他!

priority_queue<int,vector<int>,cmp> pq;
 
vector<int>就是其底层默认的容器,cmp是重载过后的(结构体or类)
初识:
#include <iostream>
#include <queue>
using namespace std;
struct cmp{
    bool operator()(int a,int b){
        return a<b; // 因为是正常比较,固为最大堆
                    // return a>b 最小堆
    }
};
int main(){
    priority_queue<int,vector<int>,cmp> pq; 
    pq.push(3);
    pq.push(2);
    pq.push(1);
    while(pq.size()!=0){
        cout<<pq.top()<<endl;
        pq.pop();
    }
    return 0;
}

我看了很多博客,大多数博主,重载的符号都是<符号,而不是(),咱们就在这里说一说。

重载 operator< 的优点

  • 简单直观:直接在类中定义比较逻辑,适合类本身需要一个固定的排序规则。

  • 通用性:不仅适用于 priority_queue,还适用于其他需要比较的场景,如 sort 函数。

自定义比较函数对象的优点

  • 灵活性:可以在不修改类定义的情况下,为不同的排序需求提供多种比较逻辑。

  • 避免全局污染:比较逻辑封装在函数对象中,不会影响类的全局比较行为。

源码中的关键逻辑

priority_queue的底层实现中,堆调整(如pushpop)时会调用比较器:

// 伪代码:堆的上浮操作
void _upheap(int i) {
    while (i > 0) {
        int parent = (i-1)/2;
        if (Compare()(heap[parent], heap[i])) { // 调用operator()
            swap(heap[parent], heap[i]);
            i = parent;
        } else break;
    }
}
底层的简单实现:

其实一下代码,就是的低配版,只要知道什么是堆排序,就能够轻松解决优先队列。

#include <iostream>
#include <vector>

template <typename T>
class PriorityQueue {
private:
    std::vector<T> heap;

    // 上浮操作,用于维护堆的性质
    void siftUp(int index) {
        while (index > 0) {
            int parent = (index - 1) / 2;
            if (heap[parent] >= heap[index]) {
                break;
            }
            std::swap(heap[parent], heap[index]);
            index = parent;
        }
    }

    // 下沉操作,用于维护堆的性质
    void siftDown(int index) {
        int size = heap.size();
        while (true) {
            int leftChild = 2 * index + 1;
            int rightChild = 2 * index + 2;
            int largest = index;

            if (leftChild < size && heap[leftChild] > heap[largest]) {
                largest = leftChild;
            }
            if (rightChild < size && heap[rightChild] > heap[largest]) {
                largest = rightChild;
            }

            if (largest == index) {
                break;
            }

            std::swap(heap[index], heap[largest]);
            index = largest;
        }
    }

public:
    // 判断队列是否为空
    bool empty() const {
        return heap.empty();
    }

    // 获取队列的大小
    size_t size() const {
        return heap.size();
    }

    // 获取堆顶元素
    T top() const {
        if (empty()) {
            throw std::out_of_range("Priority queue is empty");
        }
        return heap[0];
    }

    // 插入元素
    void push(const T& value) {
        heap.push_back(value);
        siftUp(heap.size() - 1);
    }

    // 删除堆顶元素
    void pop() {
        if (empty()) {
            throw std::out_of_range("Priority queue is empty");
        }
        std::swap(heap[0], heap.back());
        heap.pop_back();
        siftDown(0);
    }
};

int main() {
    PriorityQueue<int> pq;
    pq.push(3);
    pq.push(1);
    pq.push(2);

    std::cout << "Top element: " << pq.top() << std::endl;
    pq.pop();
    std::cout << "Top element after pop: " << pq.top() << std::endl;

    return 0;
}    
大纲

一、前 K 个高频元素-(解析)-结合unordered_map复合运用,了解map的最小单位pair

二、滑动窗口最大值-(解析)-pair<int,int>复合运用,滑动窗口实践

三、 游戏 蓝桥真题-(解析)-相当于上两道题结合运用

( •̀ ω •́ )✧点击这里,继续学习其他模块吧!

题目:
一、前 K 个高频元素

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

示例 1:

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

示例 2:

输入: nums = [1], k = 1
输出: [1]

提示:

  • 1 <= nums.length <= 105
  • k 的取值范围是 [1, 数组中不相同的元素的个数]
  • 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的
class Solution {
    struct cmp{
        bool operator()(pair<int,int> p1, pair<int,int> p2){
            return p1.second<p2.second; // 正常比较
        }
    };
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int,int> umap;
        for(int i : nums){
            umap[i]++;
        }
        priority_queue<pair<int,int>,vector<pair<int,int>>,cmp>pq;
        for(auto it = umap.begin(); it!=umap.end(); ++it){
            pq.push(*it);
        }
        vector<int> vec;
        while(k--){
            vec.push_back(pq.top().first);
            pq.pop();
        }
        return vec;
    }
};
二、滑动窗口最大值

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值 

示例 1:

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

示例 2:

输入:nums = [1], k = 1
输出:[1]

提示:

  • 1 <= nums.length <= 105
  • -104 <= nums[i] <= 104
  • 1 <= k <= nums.length
class Solution {
    struct cmp{
        bool operator()(const pair<int,int>& p1,const pair<int,int>& p2){
            return p1.first<p2.first;
        }
    };
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
          vector<int> vec;
        priority_queue<pair<int,int>,vector<pair<int,int>>,cmp> p;
        for(int i=0; i<k; ++i){
            p.push({nums[i],i});
        }
        // 添加第一个滑动窗口的最大值
        vec.push_back(p.top().first);
        for(int i=k; i<nums.size(); ++i){
            p.push({nums[i],i});
            while(p.top().second<=i-k) p.pop();
            vec.push_back(p.top().first);
        }
        return vec;
    }
};
三、 游戏 蓝桥真题

问题描述

熊大和熊二在玩游戏。他们将 nn 个正整数 a1,a2,...,ana1​,a2​,...,an​ 排成一行,然后各用一个长度为 kk 的框在这个数组中各自随机框选出一段长度为 kk 的连续子序列(随机框选指在合法的 n−k+1nk+1 个连续子序列中均匀随机)。熊大记录了他框出的 kk 个数中的最大值 PP,熊二记录了他框出的 kk 个数的最小值 QQ,他们突然有个疑问:P−QPQ 的期望是多少?

2024-11-27 Update:Java 时限调整至 1s

输入描述

输入共 22 行。

第一行为两个正整数 n,kn,k

第二行为 nn 个由空格隔开的正整数 a1,a2,...,ana1​,a2​,...,an​。

输出描述

输出共 11 行,一个浮点数(请保留两位小数)。

样例输入

3 2
1 2 3 

样例输出

1.00

样例说明

一共有四种情况:

熊大框出 [1,2][1,2],P=2P=2;熊二框出 [1,2][1,2],Q=1Q=1,P−Q=1PQ=1。

熊大框出 [1,2][1,2],P=2P=2;熊二框出 [2,3][2,3],Q=2Q=2,P−Q=0PQ=0。

熊大框出 [2,3][2,3],P=3P=3;熊二框出 [1,2][1,2],Q=1Q=1,P−Q=2PQ=2。

熊大框出 [2,3][2,3],P=3P=3;熊二框出 [2,3][2,3],Q=2Q=2,P−Q=1PQ=1。

所以 P−QPQ 的期望为(1+0+2+1)/4=1.00(1+0+2+1)/4=1.00。

评测用例规模

对于 20%20% 的数据,保证 n≤102n≤102。

对于 40%40% 的数据,保证 n≤103n≤103。

对于 100%100% 的数据,保证 n≤105n≤105,0<ai≤1090<ai​≤109,0<k≤n0<kn

#include <iostream>
#include <queue>
#define ll long long
using namespace std;
struct max_cmp{
    bool operator()(const pair<ll,int>& p1,const pair<ll,int>& p2){ // 最大堆
        return p1.first<p2.first;
    }
};
struct min_cmp{
    bool operator()(const pair<ll,int>& p1,const pair<ll,int>& p2){ // 最小堆
        return p1.first>p2.first;
    }
};
int main()
{
    priority_queue<pair<ll,int>,vector<pair<ll,int>>,max_cmp> max_p;
    priority_queue<pair<ll,int>,vector<pair<ll,int>>,min_cmp> min_p;
    int n,k;
    cin>>n>>k;
    for(int i=0; i<k; ++i){ // 拓展
        ll cur;
        cin>>cur;
        max_p.push({cur,i});
        min_p.push({cur,i});
    }
    int P,Q;
    P = max_p.top().first;
    Q = min_p.top().first;
    ll sum = P-Q;
    for(int i=k; i<n; ++i){ // 增加
        while(!max_p.empty()&&max_p.top().second<=i-k) max_p.pop();
        while(!min_p.empty()&&min_p.top().second<=i-k) min_p.pop();
        ll cur;
        cin>>cur;
        max_p.push({cur,i});
        min_p.push({cur,i});
        P = max_p.top().first;
        Q = min_p.top().first;
        sum += P-Q;
    }
    printf("%.2lf",(double)sum/(n-k+1));
    return 0;
}

知识点:
重载函数与重载运算符 :: 基础巩固 ::

这里我着重要掌握的是,重载运算符

重载函数

在同一作用域中,可以声明几个功能类似的同名函数。但是形式参数(指参数类型、顺序等)必须不同。如下:

#include <iostream>
using namespace std;\
struct printData{
public:
    void print(int i){
        cout<<i<<endl;
    }
    void print(double i){
        cout<<i<<endl;
    }
    void print(char c[]){
        cout<<c<<endl; 
    }
};
int main(){
    printData pd;
    pd.print(3);

    return 0;
}

重载运算符

语法格式:

class 类名
{
public:
    返回类型 operator 运算符 (参数列表)
    {
        // 运算符的具体运算过程
    }
}

普通成员函数,以标识符作为函数名。
而重载函数,以 "operator 函数名" 作为函数名。其中operator 表示这是一个重载的运算符。
而后的运算符,就是我们要定义的符号。

如:

a+b;
实际等同于
a.operator + (b)

实例操作:

class Box
{
      private:
      double length;      // 长度
      double breadth;     // 宽度
      double height;      // 高度

      // 重载 + 运算符,用于把两个 Box 对象相加
      Box operator+(const Box& b)
      {
         Box box;
         box.length = this->length + b.length;
         box.breadth = this->breadth + b.breadth;
         box.height = this->height + b.height;
         return box;
      }

};

Box3 = Box1 + Box2;

为啥用 this 时,有->:
在 C++ 中,每一个非静态成员函数都有一个隐含的指针形参,即this指针。
this指针指向调用该成员函数的对象,它是一个常量指针,其类型为类名* const 。

期望 :: 数学知识 ::

期望的由来赌硬币

基础忘记时,可以看看堵硬币


借鉴博客:

1、C++ 重载运算符和重载函数

2、C++中的运算符重载(非常详细)

3、【原创】优先队列 priority_queue 详解


( •̀ ω •́ )✧点击这里,继续学习其他模块吧! 

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

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

相关文章

《信息系统安全》(第一次上机实验报告)

实验一 &#xff1a;网络协议分析工具Wireshark 一 实验目的 学习使用网络协议分析工具Wireshark的方法&#xff0c;并用它来分析一些协议。 二实验原理 TCP/IP协议族中网络层、传输层、应用层相关重要协议原理。网络协议分析工具Wireshark的工作原理和基本使用规则。 三 实…

简要分析IPPROTO_TCP参数

IPPROTO_TCP是操作系统或网络编程中定义的一个 协议号常量&#xff0c;用于标识 传输控制协议&#xff08;TCP&#xff09;。其核心作用是 在传输层指定使用TCP协议&#xff0c;确保数据通过TCP的可靠传输机制进行通信。 一、定义与值 头文件&#xff1a;定义在<netinet/in.…

JavaScript与客户端开发

1、简介 简单的讲&#xff0c;JavaScript是一种脚本语言&#xff0c;为网站提供了一种在客户端运行程序的手段&#xff0c;通过它可以实现客户端数据验证、网页特效等功能。 JavaScript是一种基于对象和事件驱动&#xff08;不懂啥意思&#xff0c;暂不管它&#xff09;&…

基于CNN的FashionMNIST数据集识别5——GoogleNet模型

源码 import torch from torch import nn from torchsummary import summaryclass Inception(nn.Module):def __init__(self, in_channels, c1, c2, c3, c4):super().__init__()self.ReLu nn.ReLU()#路径1self.p1_1 nn.Conv2d(in_channelsin_channels, out_channelsc1, kern…

JVM垃圾回收笔记01-垃圾回收算法

文章目录 前言1. 如何判断对象可以回收1.1 引用计数法1.2 可达性分析算法查看根对象哪些对象可以作为 GC Root ?对象可以被回收&#xff0c;就代表一定会被回收吗&#xff1f; 1.3 引用类型1.强引用&#xff08;StrongReference&#xff09;2.软引用&#xff08;SoftReference…

【初探数据结构】树与二叉树

&#x1f4ac; 欢迎讨论&#xff1a;在阅读过程中有任何疑问&#xff0c;欢迎在评论区留言&#xff0c;我们一起交流学习&#xff01; &#x1f44d; 点赞、收藏与分享&#xff1a;如果你觉得这篇文章对你有帮助&#xff0c;记得点赞、收藏&#xff0c;并分享给更多对数据结构感…

蓝桥杯备考:二分答案之路标设置

最大距离&#xff0c;找最小空旷指数值&#xff0c;我们是很容易想到用二分的&#xff0c;我们再看看这个答案有没有二段性 是有这么个二段性的&#xff0c;我们只要二分就行了&#xff0c;但是二分的check函数是有点不好想的&#xff0c;我们枚举空旷值的时候&#xff0c;为了…

回调方法传参汇总

文章目录 0. 引入问题1. 父子组件传值1.1 父传子&#xff1a;props1.2 子传父&#xff1a;$emit1.3 双向绑定&#xff1a;v-model 2. 多个参数传递3. 父组件监听方法传递其他值3.1 $event3.2 箭头方法 4. 子组件传递多个参数&#xff0c;父组件传递本地参数4.1 箭头函数 … 扩…

XSS基础靶场练习

目录 1. 准备靶场 2. PASS 1. Level 1&#xff1a;无过滤 源码&#xff1a; 2. level2&#xff1a;转HTML实体 htmlspecialchars简介&#xff1a; 源码 PASS 3. level3:转HTML深入 源码&#xff1a; PASS 4. level4:过滤<> 源码&#xff1a; PASS: 5. level5:过滤on 源码…

Redis核心机制(一)

目录 Redis的特性 1.速度快 2.以键值对方式进行存储 3.丰富的功能 4.客户端语言多 5.持久化 6.主从复制 7.高可用和分布式 Redis使用场景 Redis核心机制——持久化 RDB bgsave执行流程 ​编辑 AOF AOF重写流程 3.混合持久化&#xff08;RDBAOF&#xff09; Red…

QGroupBox取消勾选时不禁用子控件

默认情况下&#xff0c;QGroupBox取消勾选会自动禁用子控件&#xff0c;如下图所示 那么如何实现取消勾选时不禁用子控件呢&#xff1f; 实现很简单&#xff0c;直接上代码了 connect(ui->groupBox, &QGroupBox::toggled, this, [](bool checked){if (checked false){…

MyBatis-Plus 自动填充:优雅实现创建/更新时间自动更新!

目录 一、什么是 MyBatis-Plus 自动填充&#xff1f; &#x1f914;二、自动填充的原理 ⚙️三、实际例子&#xff1a;创建时间和更新时间字段自动填充 ⏰四、注意事项 ⚠️五、总结 &#x1f389; &#x1f31f;我的其他文章也讲解的比较有趣&#x1f601;&#xff0c;如果喜欢…

canvas数据标注功能简单实现:矩形、圆形

背景说明 基于UI同学的设计&#xff0c;在市面上找不到刚刚好的数据标注工具&#xff0c;遂决定自行开发。目前需求是实现图片的矩形、圆形标注&#xff0c;并获取标注的坐标信息&#xff0c;使用canvas可以比较方便的实现该功能。 主要功能 选中图形&#xff0c;进行拖动 使…

【UI设计】一些好用的免费图标素材网站

阿里巴巴矢量图标库https://www.iconfont.cn/国内最大的矢量图标库之一&#xff0c;拥有 800 万 图标资源。特色功能包括团队协作、多端适配、定制化编辑等&#xff0c;适合企业级项目、电商设计、中文产品开发等场景。IconParkhttps://iconpark.oceanengine.com/home字节跳动…

ubuntu 解挂载时提示 “umount: /home/xx/Applications/yy: target is busy.”

问题如题所示&#xff0c;我挂载一个squanfs文件系统到指定目录&#xff0c;当我使用完后&#xff0c;准备解挂载时&#xff0c;提示umount: /home/xx/Applications/yy: target is busy.&#xff0c;具体的如图所示&#xff0c; 这种提示通常是表明这个路径的内容正在被某些进…

一条不太简单的TEX学习之路

目录 rule raisebox \includegraphics newenviro 、\vspace \stretch \setlength 解释&#xff1a; 总结&#xff1a; 、\linespread newcommand \par 小四 \small simple 、mutiput画网格 解释&#xff1a; 图案解释&#xff1a; xetex pdelatex etc index 报…

Matplotlib完全指南:数据可视化从入门到实战

目录 引言 一、环境配置与基础概念 1.1 安装Matplotlib 1.2 导入惯例 1.3 两种绘图模式 二、基础图形绘制 2.1 折线图&#xff08;Line Plot&#xff09; 2.2 柱状图&#xff08;Bar Chart&#xff09; 三、高级图表类型 3.1 散点图&#xff08;Scatter Plot&#xff…

在大数据开发中ETL是指什么?

hello宝子们...我们是艾斯视觉擅长ui设计和前端数字孪生、大数据、三维建模、三维动画10年经验!希望我的分享能帮助到您!如需帮助可以评论关注私信我们一起探讨!致敬感谢感恩! 在数字经济时代&#xff0c;数据已成为企业最核心的资产。然而&#xff0c;分散在业务系统、日志文件…

OAuth 2.0认证

文章目录 1. 引言1.1 系列文章说明1.2 OAuth 2.0 的起源与演变1.3 应用场景概览 2. OAuth 2.0 核心概念2.1 角色划分2.2 核心术语解析 3. 四种授权模式详解3.1 授权码模式&#xff08;Authorization Code Grant&#xff09;3.1.1 完整流程解析3.1.2 PKCE 扩展&#xff08;防止授…

Kubernetes的Replica Set和ReplicaController有什么区别

ReplicaSet 和 ReplicationController 是 Kubernetes 中用于管理应用程序副本的两种资源&#xff0c;它们有类似的功能&#xff0c;但 ReplicaSet 是 ReplicationController 的增强版本。 以下是它们的主要区别&#xff1a; 1. 功能的演进 ReplicationController 是 Kubernete…