【C++】优先级队列介绍与模拟实现

💞💞 前言

hello hello~ ,这里是大耳朵土土垚~💖💖 ,欢迎大家点赞🥳🥳关注💥💥收藏🌹🌹🌹
在这里插入图片描述

💥个人主页:大耳朵土土垚的博客
💥 所属专栏:C++入门至进阶
这里将会不定期更新有关C++的内容,希望大家多多点赞关注收藏💖💖

目录

  • 💞💞 前言
  • 1.什么是优先级队列
  • 2.仿函数的介绍
  • 3.优先级队列使用
  • 4.优先级队列模拟实现
    • ✨堆向下调整算法
    • ✨堆向上调整算法
    • ✨仿函数
    • ✨优先级队列模拟实现
    • ✨测试代码
  • 5.结语

1.什么是优先级队列

优先级队列是一种特殊的队列,其中的元素都被赋予了优先级。元素的优先级决定了它们在队列中的顺序。在优先级队列中,元素按照优先级从高到低的顺序出队列。

优先级队列可以通过不同的数据结构来实现,常用的有二叉堆、二叉搜索树和斐波那契堆等。

优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。注意:默认情况下priority_queue是大堆

2.仿函数的介绍

仿函数(Functor)是一种重载了函数调用运算符()的类或结构体,它可以像函数一样被调用。通过重载函数调用运算符,仿函数可以实现自定义的操作行为。

仿函数可以像普通函数一样接受参数,并返回结果。它可以用于函数对象的传递、函数指针的替代、算法的灵活性增加等场景。

使用仿函数的步骤如下:

  1. 定义一个仿函数类或结构体,重载函数调用运算符()。可以根据需要定义构造函数、析构函数和其他成员函数。
  2. 在代码中创建仿函数对象。仿函数对象可以像函数一样调用,传入参数,并返回结果。

下面是一个示例,演示了使用仿函数实现求平方的功能:

// 定义仿函数类
struct Square {int operator()(int x) const {return x * x;}
};int main() {Square square;  // 创建仿函数对象int result = square(5);  // 调用仿函数// result = 25return 0;
}

在上述示例中,Square 是一个仿函数类,重载了函数调用运算符()。通过创建 Square 对象 square,并像函数一样调用
square(5),可以得到5的平方值25。

通过仿函数,我们可以实现更灵活和自定义的操作行为,并且可以与STL算法等标准库函数配合使用,提高代码的可读性和可维护性。

3.优先级队列使用

函数声明接口说明
priority_queue()/priority_queue(first,last)构造一个优先级队列
empty( )检测优先级队列是否为空,是返回true,否则返回false
top( )返回优先级队列中最大(最小元素),即堆顶元素
push(x)在优先级队列中插入元素x
pop()删除优先级队列中最大(最小)元素,即堆顶元素

测试代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<vector>
#include <queue>
#include <functional> // greater算法的头文件
void TestPriorityQueue()
{// 默认情况下,创建的是大堆vector<int> v = { 3,2,7,6,0,4,1,9,8,5 };priority_queue<int> q1;//使用范围for入队列for (auto& e : v)q1.push(e);while (!q1.empty()){cout << q1.top() << " ";q1.pop();}cout << endl;// 如果要创建小堆,将第三个模板参数换成greater比较方式priority_queue<int, vector<int>, greater<int>> q2(v.begin(), v.end());while (!q2.empty()){cout << q2.top() << " ";q2.pop();}
}int main()
{TestPriorityQueue();return 0;
}

结果如下:
在这里插入图片描述

因为实现大堆还是小堆的底层逻辑是不一样的,对应得代码也有些许差异,但为了减少代码的量,提高程序员编程的效率,我们就可以在上述优先级队列的类模板中再传入一个仿函数,对于不同的堆传不同的仿函数类以实现不同的需求;

模板不能直接传入函数,但是可以传类型,可以是自定义类型也可以是内置类型,所以可以传入一个仿函数(它本质是一个类)

4.优先级队列模拟实现

优先级队列模拟实现和队列类似,所不同的是每次插入数据后都会使用算法将队列中的数据调整为一个堆,每次删除也是删除堆顶元素,然后将剩余的元素再次调整为一个堆,这样每次堆顶的元素就是所有数据中优先级最高的那一个了,对于堆算法有疑问的可以点击数据结构——堆的介绍与实现查看

✨堆向下调整算法

现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。
向下调整算法有一个前提:左右子树必须是一个堆,才能调整。

int array[] = {27,15,19,18,28,34,65,49,25,37};

在这里插入图片描述

🥳🥳 下面介绍向下调整为小堆
前提条件——左右子树都是小堆

//堆向下调整算法(小堆)
void AdjustDown(HPDataType* a, int n,int parent)
{int child = parent * 2 + 1;//向下调整while (parent < n){//找到较小的孩子节点if (child + 1 < n && a[child] > a[child + 1]){child++;}if (a[child] < a[parent]){Swap(&a[child], &a[parent]);parent = child;child = child * 2 + 1;}elsebreak;}
}

因为要调整为小堆,所以要找到孩子中较小的一个进行比较;
如果父节点小于较小的孩子节点则直接break不需要调整,因为向下调整的前提条件是——左右子树都是小堆
调整前:
在这里插入图片描述
调整后:在这里插入图片描述

所以我们就可以利用堆向下调整算法来将堆顶元素与最后一个元素交换后,删除交换后最后一个元素,保证除了交换后堆顶元素外,左右子树都是一个堆,然后利用堆向下调整算法将整个二叉树调整为一个堆

此外堆向下调整算法还可以将一串数据调整为一个堆:
在这里插入图片描述

当然堆向上调整算法也可以实现,只不过它的时间复杂度没有堆向下调整算法好,所以我们选择使用堆向下调整算法构建堆

✨堆向上调整算法

我们知道堆的父节点必须都大于或小于子节点,那么往一个堆中插入元素是没办法保证大于或小于其父节点的,所以我们插入之后需要调整这个二叉树来保证堆;
这里就要用到堆向上调整算法了;注意下面是小堆的调整

堆向上调整算法

//向上调整
void AdjustUp(HPDataType* a,int child)
{//找到双亲节点int parent = (child - 1) / 2;//向上调整while (child > 0){if (a[parent] > a[child]){Swap(&a[parent], &a[child]);child = parent;parent = (child - 1) / 2;}elsebreak;}
}

堆向上调整类似于向下调整也有大堆小堆之分,大家可以依照堆的向下调整自己试试看写一下大堆的向上调整

✨仿函数

有了堆向下调整算法来删除堆顶元素和建堆,以及堆向上调整算法尾插元素,我们就可以实现优先级队列了🥳🥳
但是优先级队列能否按照我们需要选择大堆还是小堆呢?
这就需要利用我们之前学习的仿函数,传入一个类模板class Compare,当我们想要建小堆的时候就选择相应的类即可

//实现大堆
template<class T>struct Less {bool operator()(const T& x, const T& y){return x < y;}};//实现小堆template<class T>struct Greater{bool operator()(const T& x, const T& y){return x > y;}};

注意这里类的名字是我们自己取的,为了和STL库里面的命名保持一致,我们使用Less建立大堆,Greater建立小堆,因为建大堆还是小堆关键就在于父节点与孩子节点比较是大于还是小于,所以我们将所有比较的地方都使用仿函数,这样就可以按照我们希望的方式来比较,如果希望是大堆,就按Less中的<来比较;小堆就按照Greater中的>来比较

这样就可以将上述仿函数传给优先级队列了:

  //优先级队列的模拟实现template<class T, class Container = vector<T>,class Compare = Less<T>>//默认是大堆class priority_queue{}

✨优先级队列模拟实现

#pragma once
#include<iostream>
using namespace std;
#include<vector>
#include <queue>
#include <functional> // greater算法的头文件
namespace tutu //防止命名冲突
{template<class T>struct Less {bool operator()(const T& x, const T& y){return x < y;}};template<class T>struct Greater{bool operator()(const T& x, const T& y){return x > y;}};//优先级队列的模拟实现template<class T, class Container = vector<T>,class Compare = Less<T>>class priority_queue{public://强制生成默认构造函数priority_queue() = default;//迭代器构造template<class InputIterator>priority_queue(InputIterator begin, InputIterator end){//入队列while (begin != end){_c.push_back(*begin);begin++;}//调整数据为堆for (int i = ((int)_c.size() - 1-1)/2; i >= 0; i--){Adjust_Down(i);}}//initializer_list构造priority_queue(initializer_list<T> il){for (const auto& e : il){_c.push_back(e);}//调整数据为堆for (int i = ((int)_c.size() - 1-1)/2; i >= 0; i--){Adjust_Down(i);}}//向上调整算法void Adjust_Up(int child){int parent = (child - 1) / 2;while (child > 0){if (_comp(_c[parent] , _c[child])){swap(_c[parent], _c[child]);child = parent;parent = (child - 1) / 2;}else{break;}}}//插入元素void push(const T& val){_c.push_back(val);Adjust_Up(_c.size() - 1);}//向下调整算法void Adjust_Down(int parent){int child = parent * 2 + 1;while (parent< _c.size()){//找更大的孩子节点if (child + 1 < _c.size() && _comp(_c[child ] , _c[child+1])){child++;}if (child<_c.size() && _comp(_c[parent] , _c[child])){swap(_c[parent], _c[child]);parent = child;child = parent * 2 + 1;}else{break;}}}//删除堆顶元素void pop(){swap(_c[0], _c[_c.size() - 1]);_c.pop_back();Adjust_Down(0);}//取堆顶元素T& top(){return _c[0];}const T& top()const{return _c[0];}size_t size() const{return _c.size();}bool empty() const{return _c.empty();}private:Container _c;//底层容器Compare _comp; //比较方式};}

✨测试代码

 //测试代码void test1(){priority_queue<int> p;p.push(1);p.push(8);p.push(3);p.push(4);p.push(6);p.push(2);while (!p.empty()){cout << p.top() << " ";p.pop();}}//迭代器构造测试void test2(){      vector<int> v{ 1, 7, 8, 4, 5, 9, 2, 3, 6 };priority_queue<int> p(v.begin(), v.end());while (!p.empty()){cout << p.top() << " ";p.pop();}}//initializer_list构造测试代码void test3(){ priority_queue<int> p = { 1,4,7,5,3,9,2,6,8 };while (!p.empty()){cout << p.top() << " ";p.pop();}}

我们使用test1测试,结果如下:

在这里插入图片描述

发现每次取的都是堆顶的元素(也就是数据中优先级最高的那一个)

5.结语

前面我们学习过栈和队列,优先级队列和它们类似,所不同的是每次插入数据都需要使用堆算法来调整建堆,删除堆顶数据后也需要进行建堆,这样每次堆顶元素都是优先级最高的那个元素,以上就是优先级队列的所有内容啦~ 完结撒花 ~🥳🎉🎉

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

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

相关文章

Hadoop3:MapReduce之InputFormat数据输入过程整体概览(0)

一、MapReduce中数据流向 二、MapTask并行度 1、原理概览 数据块&#xff1a;Block是HDFS物理上把数据分成一块一块。数据块是HDFS存储数据单位。 数据切片&#xff1a;数据切片只是在逻辑上对输入进行分片&#xff0c;并不会在磁盘上将其切分成片进行存储。数据切片是MapRed…

哇噻,Zabbix7.0 LTS正式发布!功能又进化了!

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 作者&#xff1a;IT邦德 中国DBA联盟(ACDU)成员&#xff0c;10余年DBA工作经验&#xff0c; Oracle、PostgreSQL ACE CSDN博客专家及B站知名UP主&#xff0c;全网粉丝10万 擅长主流Oracle、My…

全平台自定义小程序源码系统 一个后台控制7端 自主设计属于你的小程序 前后端带完整的安装代码包以及搭建教程

系统概述 在当今数字化时代&#xff0c;小程序以其轻量级、跨平台、即用即走的特点&#xff0c;成为企业、个人及开发者们追捧的热门工具。为了满足不同用户的需求&#xff0c;小编给大家分享一款全平台自定义小程序源码系统。该系统通过一套强大的后台管理系统&#xff0c;实…

游泳预约报名小程序开发源码案例模板之前端功能介绍

越来越多游泳馆使用线上预约报名管理系统&#xff0c;以此来提升游泳馆预约的便捷性以及管理的效率。馆客多小程序是一款实用、便捷的线上运动服务平台&#xff0c;可以让用户轻松预订游泳馆场地并享受自助线上服务&#xff0c;同时也减轻了游泳馆运营压力&#xff0c;提高游泳…

XLA - 加速线性代数

文章目录 一、关于 XLAXLA目标XLA 运作方式 二、Community沟通渠道其他资源存储库 一、关于 XLA XLA : Accelerated Linear Algebra github : https://github.com/openxla/xlaOpenXLA Community : https://github.com/openxla/communityXLA - TensorFlow : https://tensorflo…

Flink SQL查询语法部分详解(提供需求、数据练习复现)

一、Hints 动态表选择&#xff1a;可以在查询表的时候动态修改表的参数配置 1、读取kafka的数据建表 CREATE TABLE students (id STRING,name STRING,age INT,sex STRING,clazz STRING ) WITH (connector kafka,topic students, -- 指定topicproperties.bootstrap.servers …

高效扫码点餐:简餐茶饮外卖新体验

前言 在快节奏的现代生活中&#xff0c;高效便捷的扫码点餐系统正逐渐成为简餐茶饮行业的新宠。这一系统不仅提升了顾客的点餐体验&#xff0c;还优化了门店的运营效率&#xff0c;特别是基于总部多门店的连锁模式&#xff0c;更是将这一优势发挥得淋漓尽致。 一、这款扫码点餐…

如何通过PHP语言实现远程控制多路照明

如何通过PHP语言实现远程控制多路照明呢&#xff1f; 本文描述了使用PHP语言调用HTTP接口&#xff0c;实现控制多路照明&#xff0c;通过多路控制器&#xff0c;可独立远程控制多路照明。 可选用产品&#xff1a;可根据实际场景需求&#xff0c;选择对应的规格 序号设备名称厂…

软理复习范围

1.直觉主义逻辑常采用三值逻辑来处理命题的真值&#xff0c;包括以下三个真值&#xff1a; 真&#xff08;True&#xff09;&#xff1a;表示命题是确定为真的。假&#xff08;False&#xff09;&#xff1a;表示命题是确定为假的。未知&#xff08;Unknown&#xff09;&#…

Prism 入门02,区域介绍

一.区域概念和使用方式 什么是区域(Region)?区域,在Prism 框架中,区域是模块化的核心功能之一,其主要作用是降低应用程序和模块之间的耦合度 。使用方式:在应用程序的界面中,划分出某块区域,并为这个区域定义一个唯一的区域名称。那么通过这个区域名称,应用程序就可以…

el-tabel名称排序问题

el-tabel排序 最终实现功能如下&#xff1a; 排序限制为&#xff1a; 文件夹>普通文件 数字&#xff08;0->9&#xff09;->大写字母&#xff08;A->Z&#xff09;->小写字母&#xff08;a->z&#xff09;->中文拼音&#xff08;a->z&#xff09; 正序…

Unity开发Cosmos使用BNG Framework获取按键信息

Unity开发Cosmos使用BNG Framework获取按键信息 1、新建一个脚本&#xff0c;复制下面代码 using BNG;[Header("Input")]//[Tooltip("The key(s) to use to toggle locomotion type")]public List<ControllerBinding> locomotionToggleInput new …

处理无法拉取GitHub库的解决方案

提交和拉取github上的库总是失败&#xff0c;这里记录一下如何使用代理解决。 首先找到端口&#xff0c;记住它的端口 然后使用git命令 # HTTP/HTTPS 协议 git config ––global http.url.proxy http://127.0.0.1:port # 以 Github 为例 git config ––global http.https:/…

短网址生成原理及使用

生成短网址介绍&#xff1a; 一、定义 短网址&#xff08;Short URL&#xff09;是形式上比较短的网址&#xff0c;它通过将原始冗长的网址进行缩短&#xff0c;方便用户分享和记忆。短网址的生成主要依赖于特定的算法和服务&#xff0c;通过后端服务转向来实现网址的缩短。 …

什么是人机协同翻译

什么是人机协同翻译 序什么是人机协同翻译账号绑定服务开通文档翻译图片翻译体验感受及建议 序 什么是人机协同翻译&#xff0c;为什么会需要人机协同翻译&#xff0c;以及人机协同翻译的效果&#xff0c;应用场景等&#xff0c;本文将关于这些内容一一解答。 什么是人机协同…

大语言模型RAG-将本地大模型封装为langchain的chat model(三)

大语言模型RAG-将本地大模型封装为langchain的chat model&#xff08;三&#xff09; 往期文章&#xff1a; 大语言模型RAG-技术概览 (一) 大语言模型RAG-langchain models (二) 上一期langchain还在0.1时代&#xff0c;这期使用的langchain v0.2已经与之前不兼容了。 本期介…

【调试笔记-20240604-Linux-为 OpenWrt LuCI 界面添加多语言支持】

调试笔记-系列文章目录 调试笔记-20240604-Linux-为 OpenWrt LuCI 界面添加多语言支持 文章目录 调试笔记-系列文章目录调试笔记-20240604-Linux-为 OpenWrt LuCI 界面添加多语言支持 前言一、调试环境操作系统&#xff1a;Ubuntu 22.04.4 LTS编译环境调试目标 二、调试步骤预…

mysql使用sorce、load 导入数据

1、本机安装mysql https://blog.csdn.net/weixin_52687711/article/details/130509902 注意&#xff1a;修改安装路径 配置环境变量 2. 使用sorce命令导入数据 https://blog.csdn.net/2301_82257317/article/details/138332048 使用mysql命令框链接目标数据库 以管理员身份运…

Echarts柱状图数据太多,自定义长度之后,自适应浏览器缩放

不知道是不是最优解&#xff0c;但是当前解决了我遇到的问题&#xff0c;如有更好的方法&#xff0c;希望看到这篇文章的同学可以不吝指导一番&#xff0c;非常感谢 1、问题描述&#xff1a; 因Ecahrts柱状图数据有时多有时少&#xff0c;所以在数据达到一定程度之后&#xff…

第54期|GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以找…