【C++入门到精通】C++入门 —— priority_queue(STL)优先队列

news2024/10/6 12:25:54

在这里插入图片描述

阅读导航

  • 前言
  • 一、priority_queue简介
    • 1. 概念
    • 2. 特点
  • 二、priority_queue使用
    • 1. 基本操作
    • 2. 底层结构
  • 三、priority_queue模拟实现
    • ⭕ C++代码
    • ⭕priority_queue中的仿函数
  • 总结
  • 温馨提示

前言

⭕文章绑定了VS平台下std::priority_queue的源码,大家可以下载了解一下😍

前面我们讲了C语言的基础知识,也了解了一些数据结构,并且讲了有关C++的命名空间的一些知识点以及关于C++的缺省参数、函数重载,引用 和 内联函数也认识了什么是类和对象以及怎么去new一个 ‘对象’ ,以及学习了几个STL的结构也相信大家都掌握的不错,接下来博主将会带领大家继续学习有关C++比较重要的知识点—— priority_queue(STL)优先队列。下面话不多说坐稳扶好咱们要开车了😍

一、priority_queue简介

⭕官方文档
在这里插入图片描述

1. 概念

priority_queue是C++标准库中的一个容器适配器(container adapter),用于实现优先队列(priority queue)的数据结构。优先队列是一种特殊的队列,其中的元素按照一定的优先级进行排序,每次取出的元素都是优先级最高的。它的底层实现通常使用堆(heap)数据结构。

在C++中,priority_queue模板类定义在<queue>头文件中,可以通过指定元素类型和比较函数来创建不同类型的优先队列。比较函数用于确定元素的优先级,可以是函数指针、函数对象或Lambda表达式。

⭕需要注意的是,默认情况下,priority_queue使用std::less作为比较函数,即元素的优先级按照从大到小的顺序排列。如果需要按照从小到大的顺序排列,可以使用std::greater作为比较函数。

2. 特点

  1. 优先级排序:priority_queue中的元素按照一定的优先级进行排序。默认情况下,元素的优先级按照从大到小的顺序排列,也可以通过自定义的比较函数来指定不同的排序方式。

  2. 自动排序:在插入元素时,priority_queue会根据元素的优先级自动进行排序。每次插入新元素时,都会将新元素放置在正确的位置上。

  3. 取出优先级最高元素:priority_queue提供了一种方便的方式来取出优先级最高的元素。使用top()函数可以访问优先级最高的元素,而使用pop()函数可以将该元素从队列中移除。

  4. 底层实现采用堆:priority_queue通常使用堆(heap)数据结构来实现。堆是一种具有特定性质的二叉树,可以高效地插入新元素和取出优先级最高的元素。

  5. 动态大小:priority_queue的大小可以根据需要进行动态调整。可以随时插入新元素和删除已有元素,并在必要时自动重新排序。

⭕需要注意的是,priority_queue并不支持直接访问和修改除了优先级最高元素外的其他元素。如果需要对特定元素进行操作,通常需要先将其取出,然后再进行操作,最后再将其放回优先队列中。

二、priority_queue使用

1. 基本操作

  1. 包含头文件:首先,在使用priority_queue之前,你需要包含<queue>头文件。
#include <queue>
  1. 定义容器和比较函数:然后,你需要定义一个priority_queue对象,并指定元素类型和可选的比较函数(默认为std::less)。
std::priority_queue<int, std::vector<int>, std::less<int>> pq;

上面的示例定义了一个存储整数的优先队列,使用std::less作为比较函数,以便元素按照从大到小的顺序排列。

  1. 插入元素:你可以使用push()函数插入元素到priority_queue中。插入的元素会根据其优先级自动进行排序。
pq.push(3);
pq.push(1);
pq.push(4);
pq.push(1);

在上面的示例中,我们依次插入了4个整数到优先队列中。

  1. 访问和移除元素:你可以使用top()函数访问优先级最高的元素,使用pop()函数移除优先级最高的元素。
std::cout << pq.top() << std::endl;  // 访问优先级最高的元素
pq.pop();                           // 移除优先级最高的元素

在上面的示例中,我们先输出了优先级最高的元素,然后将其从队列中移除。

  1. 检查队列是否为空:你可以使用empty()函数来检查priority_queue是否为空。
if (pq.empty()) {
    // 队列为空
} else {
    // 队列不为空
}

下面的代码,演示了如何使用priority_queue

#include <queue>
#include <iostream>

int main() {
    std::priority_queue<int, std::vector<int>, std::less<int>> pq;

    pq.push(3);
    pq.push(1);
    pq.push(4);
    pq.push(1);

    while (!pq.empty()) {
        std::cout << pq.top() << " ";
        pq.pop();
    }

    return 0;
}

输出结果为:4 3 1 1

在上面的代码中,我们创建了一个存储整数的优先队列pq,并依次插入了4个元素。然后,我们使用top()函数和pop()函数访问和移除元素,最后使用empty()函数检查队列是否为空。

2. 底层结构

priority_queue的底层通常使用堆(heap)数据结构来实现。堆是一种二叉树的数据结构(堆,数据结构详细介绍链接),具有以下特点:

  1. 堆结构是一个完全二叉树(Complete Binary Tree),即除了最后一层外,其他层都必须是满的,且最后一层的结点都靠左排列。

  2. 二叉堆分为最大堆(Max Heap)和最小堆(Min Heap)两种类型。

    • 最大堆:每个父节点的值都大于或等于其子节点的值,即根节点的值最大。
    • 最小堆:每个父节点的值都小于或等于其子节点的值,即根节点的值最小。

priority_queue中,默认情况下采用最大堆实现,即优先级最高的元素存储在根节点,根节点的值最大。根据堆的性质,保证了在插入元素时,优先队列会根据元素的优先级进行自动排序,并在取出元素时能够取出优先级最高的元素。
在这里插入图片描述

三、priority_queue模拟实现

⭕ C++代码

#pragma once
#include<iostream>
using namespace std;
#include<vector>
namespace yws
{
	template<class T, class Container = vector<T>, class Compare = std::less<T>>
	class priority_queue
	{
	public:
		priority_queue()
		{}
		template <class Inputinterpreator>
		priority_queue(Inputinterpreator first, Inputinterpreator last)
		{
			while (first != last)
			{
				_con.push_back(*first);
				++first;
			}
			for (int i = (_con.size() - 1 - 1) / 2; i >= 0; --i)
			{
				adjust_down(i);
			}
		}
		void adjust_up(size_t child)
		{
			Compare com;
			size_t parent = (child - 1) / 2;
			while (child > 0)
			{
				if (com(_con[parent], _con[child]))
				{
					std::swap(_con[child], _con[parent]);
				}
				else
				{
					break;
				}
				child = parent;
				parent = (child - 1) / 2;
			}
		}
		void push(const T& x)
		{
			_con.push_back(x);
			adjust_up(_con.size() - 1);

		}
		void adjust_down(size_t parent)
		{
			Compare com;
			size_t child = (parent * 2) + 1;
			while (child < _con.size())
			{
				if (child + 1 < _con.size() && com(_con[child], _con[child + 1]))
				{
					child = child + 1;
				}
				if (com(_con[parent], _con[child]))
				{
					std::swap(_con[parent], _con[child]);
				}
				else
				{
					break;
				}
				parent = child;
				child = (parent * 2) + 1;
			}
		}
		void pop()
		{
			std::swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();

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

	private:
		Container _con;
	};
}

⭕priority_queue中的仿函数

priority_queue中,仿函数用于比较元素的优先级,并根据其返回值确定它们在队列中的位置。默认情况下,priority_queue使用std::less作为仿函数,也就是将元素按照从大到小的顺序进行排序

你可以使用不同的仿函数来改变元素的排序方式。以下是一些常见的仿函数:

  • std::less<T>:对于基本数据类型和自定义类型,默认使用<运算符进行比较,按照从大到小的顺序排序。
  • std::greater<T>:对于基本数据类型和自定义类型,默认使用>运算符进行比较,按照从小到大的顺序排序。

除了上述默认提供的仿函数外,你还可以自定义仿函数来实现自定义的元素比较规则。自定义仿函数需要满足严格弱排序(Strict Weak Ordering)的要求,即:

  • 比较关系必须是可传递的(transitive):对于任意元素a、b和c,如果a与b比较相等,b与c比较相等,则a与c比较也相等。
  • 比较关系不能是部分顺序(partial order):对于任意元素a和b,它们不能同时大于、小于或等于彼此。
  • 比较关系必须是可比较的(comparable):比较关系的结果必须对所有元素定义明确的大小关系。

以下这段代码,演示了如何自定义一个仿函数来实现元素的自定义排序方式:

#include <iostream>
#include <queue>
#include <functional>

struct Person {
    std::string name;
    int age;

    Person(const std::string& n, int a) : name(n), age(a) {}
};

// 自定义仿函数
struct CompareByAge {
    bool operator()(const Person& p1, const Person& p2) const {
        return p1.age > p2.age;  // 按照年龄从小到大排序
    }
};

int main() {
    std::priority_queue<Person, std::vector<Person>, CompareByAge> pq;

    pq.push(Person("Alice", 25));
    pq.push(Person("Bob", 30));
    pq.push(Person("Charlie", 20));

    while (!pq.empty()) {
        Person p = pq.top();
        pq.pop();
        std::cout << p.name << " - " << p.age << std::endl;
    }

    return 0;
}

输出结果为:

Charlie - 20
Alice - 25
Bob - 30

在上面的代码中,我们定义了一个名为CompareByAge的结构体,重载了函数调用运算符operator(),按照Person对象的age成员进行比较。然后,我们将CompareByAge作为优先队列的仿函数类型,并插入3个Person对象到优先队列中。最后,我们按照自定义的排序方式依次取出元素并输出。

总结

优先队列是一种特殊的队列,其中存储的元素按照一定的优先级进行排列。在priority_queue中,优先级最高的元素能够快速被访问和删除。

首先,我们介绍了priority_queue的概念和特点。它是基于堆(heap)这种数据结构实现的,通常使用最大堆来进行内部排序。最大堆保证了根节点的值最大,并且任意节点的值大于或等于其子节点的值。这种特性使得优先队列能够高效地访问和删除具有最高优先级的元素。

接着,我们深入探讨了priority_queue的使用方法。基本操作包括插入元素、删除元素、访问元素和检查队列是否为空。

底层结构是priority_queue的关键部分,它通常使用堆来实现。在堆中,通过使用数组的索引来表示节点之间的关系,能够快速定位和操作元素

最后,我们探讨了在priority_queue中使用的仿函数。仿函数用于确定元素之间的优先级,决定元素在队列中的位置。默认情况下,priority_queue使用std::less仿函数进行比较,对元素进行降序排列。你还可以选择其他仿函数或自定义仿函数来实现不同的排序方式。

温馨提示

感谢您对博主文章的关注与支持!在阅读本篇文章的同时,可以留下您宝贵的意见和反馈。如果您喜欢这篇文章,可以点赞、评论和分享给您的同学,这将对我提供巨大的鼓励和支持。另外,我计划在未来的更新中持续探讨与本文相关的内容。我会为您带来更多关于C++以及编程技术问题的深入解析、应用案例和趣味玩法等。请继续关注博主的更新,不要错过任何精彩内容!

再次感谢您的支持和关注。期待与您建立更紧密的互动,共同探索C++、算法和编程的奥秘。祝您生活愉快,排便顺畅!
在这里插入图片描述

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

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

相关文章

C#,数值计算——Ridders的多项式外推方法的计算方法与源程序

using System; namespace Legalsoft.Truffer { /// <summary> /// 通过Ridders的多项式外推方法返回函数func在点x处的导数。 /// 输入值h作为估计的初始步长&#xff1b;它不需要很小&#xff0c;而是应为x上的增量&#xff0c; /// 在此增量上func将发…

在CMD中找不到Bootrec/fixboot元素怎么办?

当您尝试在CMD中执行Bootrec/fixboot以修复Windows 7/8.1/8/10/11和Windows Server 2012等操作系统中的系统启动问题时&#xff0c;通常会遇到Bootrec/fixboot参数无效的情况。这类启动问题可能是由磁盘克隆、系统迁移、Windows更新、MBR和GPT转换等引起的。当一个问题仍然存在…

YOLO目标检测——矿石数据集图片下载分享

矿石图片&#xff0c;其中训练集包括“玄武岩”、“花岗岩”、“大理石”、“石英岩”、“煤”、“石灰石”、“砂岩”七种矿石图片。测试集包括24张相应的七种矿石图像。 数据集点击下载&#xff1a; 矿石数据集4500图片数据说明.rar

OpenGL学习路程(一)

Hello啊各位&#xff0c;鸽了挺长时间没更新&#xff0c;其实是博主找到新乐子了。 如标题所说&#xff0c;我正在学习OpenGL。 现在已经成功的调用显卡画出了一个三角形&#xff0c;这虽然不是什么大的成就&#xff0c;但已经让我很兴奋了。 我不打算在这里写出我配置openg…

Pyqt5-开源工具分解功能(文本拖拽)

开源第四篇:功能实现之拖拽功能与配置文件。 写这个功能的初衷,是因为,每次调试我都要手动敲命令,太麻烦了,想偷个懒,所以直接给这功能加上了,顺便衍生出了另一个想法,配置文件自动填写相关数据。 先看个简单的拖拽功能: 很明显吧,还是比较便捷的。所以我们本章,就在…

TextView加粗字体太粗

解决方法如下&#xff1a; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.util.AttributeSet;import androidx.annotation.Nullable; import androidx.appcompat.widget.AppCompatTextView;/*** 自定义加粗…

【真人语音】讯飞星火个人声音训练及导出工具V0.2.exe

【项目背景】 小编一直在尝试着短视频技术&#xff0c;在读文案的时候经常会读错&#xff1b;所以&#xff0c;只能用微软或者剪映的文本转语音软件。 很早之前在Github上也看到过真人人声训练的开源代码&#xff0c;尝试过一番之后&#xff0c;也是以失败告终&#xff1b;就…

若依项目的运行详细步骤

目录 一、项目的解读与获取 二、项目的运行 后端步骤(ruoyi-admin) &#xff08;一&#xff09;导入若依的2个SQL文件 版本建议 &#xff08;二&#xff09;Redis的配置 &#xff08;三&#xff09;启动后端 前端步骤(ruoyi-ui) 版本建议 &#xff08;一&#xff09;…

深入了解Git:介绍及常用命令指南

当今软件开发领域中&#xff0c;版本控制是一个至关重要的概念&#xff0c;而Git作为最流行的分布式版本控制系统&#xff0c;发挥着不可替代的作用。本文将介绍Git的基本概念以及常用命令&#xff0c;帮助你更好地理解和使用这一强大的工具。 Git简介 Git是一种分布式版本管…

结构化知识管理-20张思维导图为例子

思维导图&#xff0c;是结构化思考最高效的工具。 以中心主题为核心&#xff0c;通过各级主题的组合来呈现信息。 思维导图的可视化图形表达&#xff0c;让我们可以更加便于理解和记忆。各主题间相互连接的形式&#xff0c;展现了信息间的相关性。促进我们综合性的思考。思维导…

飞机打方块(三)特殊按钮制作

一、特殊按钮容器 1.新建PropController脚本&#xff0c;并绑定新建的特殊按钮容器节点 GameController.ts property({ type: cc.Node, displayName: "特殊按钮节点", tooltip: "特殊按钮节点&#xff0c;分别为全消无敌和菜单" })special_btn: cc.Node …

三种生成树(STP,RSTP,MSTP)的基本配置(自我理解)

目录 一、为什么要使用生成树&#xff08;STP)&#xff1a; 二、由于设备冗余而导致的问题&#xff1a; 广播风暴&#xff1a; 三、802.1D生成树基本配置 四、802.1D生成树实验 实验拓扑&#xff1a; 实验配置&#xff1a; 配置完成后&#xff0c;在SW8上观察现象&…

[Docker] Windows 下基于WSL2 安装

Docker 必须部署在 Linux 内核的系统上。如果其他系统想部署 Docker 就必须安装一个虚拟 Linux 环境。 1. 开启虚拟化 进入系统BIOS&#xff08;AMD 为 SVM&#xff1b;Intel 为 Intel-vt&#xff09;改为启用(enable) 2. 开启WSL 系统设置->应用->程序和功能->…

前端 -- 基础 HTML基本语法 结构标签介绍

HTML 语法规范 基本语法概述 : HTML 标签是由尖括号包围的关键词&#xff0c;示例 &#xff1a; <html> HTML 标签通常是成对出现的&#xff0c;例如 <html > 和 < /html > &#xff0c; 我们称为双标签。 标签对中的第一个标签是 开始标签&#xff0c;…

docker搭建私有镜像harbor

docker安装搭建私有仓库 Harbor harbor用于存储和分布docker镜像企业级registry服务器的harbor使用的是官方的docker registry(v2命名是distribution)服务去完成。 安装harhor 启动harbor 6.

【算法日志】动态规划刷题:整数拆分,n节点的BST数量(day35)

代码随想录刷题60Day 目录 前言 整数拆分 N个数节点的二叉搜索树数量 前言 今天问题的难点在于从问题中抽象出dp数组和状态转移方程。 整数拆分 int integerBreak1(int n) {vector<int> dp(n 1, 0);dp[1] 1;for (int i 2; i < n; i)for (int j 1; j < i; j…

工厂模式并不难理解

文章目录 工厂模式简单工厂模式简单工厂模式使用的场景 工厂方法模式工厂方法模式使用场景 抽象工厂模式抽象工厂模式使用场景 工厂模式 功能&#xff1a;将对象的创建交给工厂&#xff0c;我们只需要告诉工厂我们要什么对象就可以得到该对象。 目的&#xff1a;实现创建对象…

流媒体服务器SRS的搭建及QT下RTMP推流客户端的编写

一、前言 目前市面上有很多开源的流媒体服务器解决方案&#xff0c;常见的有SRS、EasyDarwin、ZLMediaKit和Monibuca。这几种的对比如下&#xff1a; &#xff08;本图来源&#xff1a;https://www.ngui.cc/zz/1781086.html?actiononClick&#xff09; 二、SRS的介绍 SRS&am…

SSRF 服务器端请求伪造

文章目录 SSRF(curl)网址访问通过file协议访问本地文件dict协议扫描内网主机开放端口 SSRF(file_get_content)网站访问http协议请求内网资源通过file协议访问本地文件 SSRF(Server-Side Request Forgery:服务器端请求伪造) 其形成的原因大都是由于服务端提供了从其他服务器应用…