【STL详解 —— priority_queue的使用与模拟实现】

news2024/11/27 12:38:26

STL详解 —— priority_queue的使用与模拟实现

  • priority_queue的使用
    • priority_queue的介绍
    • priority_queue的定义方式
    • priority_queue各个接口的使用
  • priority_queue的模拟实现
    • 仿函数
    • priority_queue的模拟实现

priority_queue的使用

priority_queue的介绍

在这里插入图片描述

std::priority_queue 是 C++ 标准库中的容器适配器,它提供了一种基于堆的优先级队列实现。优先级队列是一种特殊的队列,其中的元素按照一定的优先级顺序排列,而不是按照它们被插入的顺序。

std::priority_queue 中,插入元素时会根据元素的值自动进行排序,最大(或最小)的元素总是位于队列的顶部。对顶部元素的访问和弹出操作都是 O(1) 的时间复杂度,而插入操作则是 O(log n) 的时间复杂度。

priority_queue的定义方式

在这里插入图片描述
注意看 std::priority_queue的模板参数,总共三个参数

  1. T (Type):这是最重要的模板参数,它表示存储在优先队列中的元素类型。例如,如果要创建一个存储整数的优先队列,则可以指定 int 作为 T 的类型。

  2. Container:这是一个可选的模板参数,用于指定底层容器的类型,默认情况下是 std::vector。优先队列使用底层容器来存储元素,因此可以通过指定不同的容器类型来影响优先队列的性能和行为。

  3. Compare:这也是一个可选的模板参数,用于指定比较函数的类型,默认情况下是 std::less。比较函数用于确定优先队列中元素的顺序,例如 std::less 表示使用 < 运算符来进行比较,创建一个最大堆;而 std::greater 则表示使用 > 运算符来进行比较,创建一个最小堆。

因为后两个参数给了缺省值,所以在定义的时候可以只传一个参数,但注意,缺省只能从右往左缺,不能从左往右缺。

std::priority_queue<int> pq; // 创建一个存储整数的最大堆

std::priority_queue<int, std::deque<int>> pq; // 创建一个存储整数的最大堆,使用双端队列作为底层容器

std::priority_queue<int, std::vector<int>, std::greater<int>> pq; // 创建一个存储整数的最小堆

std::priority_queue<int, std::deque<int>, std::greater<int>> pq; // 创建一个存储整数的最小堆,使用双端队列作为底层容器,使用 std::greater 进行元素比较

priority_queue各个接口的使用

priority_queue的各个成员函数及其功能如下:

成员函数功能
push插入元素到队尾 (并排序)
pop弹出队头元素 (堆顶元素)
top访问队头元素 (堆顶元素)
size获取队列中有效元素个数
empty判断队列是否为空
swap交换两个队列的内容

下面分别演示一个建最大堆和建最小堆的priority_queue。

#include <iostream>
#include <queue>

int main() {
    // 创建一个最大堆,默认使用 std::less 比较函数
    std::priority_queue<int> maxHeap;

    // 插入元素
    maxHeap.push(3);
    maxHeap.push(1);
    maxHeap.push(5);

    // 访问顶部元素
    std::cout << "Top of maxHeap: " << maxHeap.top() << std::endl; // 输出: 5

    // 弹出顶部元素
    maxHeap.pop();

    // 创建一个最小堆,使用 std::greater 比较函数
    std::priority_queue<int, std::vector<int>, std::greater<int>> minHeap;

    // 插入元素
    minHeap.push(3);
    minHeap.push(1);
    minHeap.push(5);

    // 访问顶部元素
    std::cout << "Top of minHeap: " << minHeap.top() << std::endl; // 输出: 1

    return 0;
}

priority_queue的模拟实现

仿函数

首先先了解一下仿函数:

仿函数(Functor) 是 C++ 中的一个概念,它是一种行为类似函数的对象,可以像函数一样被调用。仿函数实际上是一个类对象,重载了函数调用运算符 operator()

因为priority_queue是基于堆实现的,在堆排中我们每次的插入元素都需要进行向上调整,每次pop都需要向下调整,所以在下面的模拟实现中我们使用仿函数来进行改写我们之前写过的向上调整和向下调整。

示例仿函数

#include <iostream>

// 定义一个仿函数类
class MyFunctor {
public:
    // 重载函数调用运算符
    int operator()(int x, int y) {
        return x + y;
    }
};

int main() {
    MyFunctor myFunctor; // 创建一个仿函数对象

    // 使用仿函数对象进行函数调用
    int result = myFunctor(3, 4);

    std::cout << "Result: " << result << std::endl; // 输出: 7

    return 0;
}

priority_queue的模拟实现

我们在之前写过数据结构|堆及其堆排序,我们之前实现的AdjustUpAdjustDown 分别如下:

void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}
}


void AdjustDown(HPDataType* a, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		if ((a[child] < a[child + 1]) && child + 1 < size)
		{
			child++;
		}
		if (a[parent] < a[child])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = child * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

这里我们因为确定元素的类型是int,所以直接使用了 < >来进行比较大小。
下面我们使用仿函数改写:
因为仿函数是一个类对象,所以我们分别使用lessgreater 来表示不同功能的仿函数类。

template<class T>
	class less
	{
	public:
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}
	};

	template<class T>
	class greater
	{
	public:
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};

由参数可知:使用类模板 class Compare 仿函数比普通函数显得更加灵活,完美的诠释了泛型编程。
后期让想改变排序方式只用更改模板参数即可。

template<class T, class Container = std::vector<T>,class Compare = less<T>>
	void adjust_up(size_t child)
		{
			Compare com;
			int parent = (child - 1) / 2;
			while (child > 0)
			{
				if (com(_con[parent], _con[child]))
				{
					std::swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}

	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;
				}
				if (com(_con[parent], _con[child]))
				{
					std::swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}

示例:

//qq::priority_queue<int,vector<int>,greater<int>> pq;
	qq::priority_queue<int> pq;		//隐式定义,默认是less,建大堆
	pq.push(1);
	pq.push(3);
	pq.push(2);
	pq.push(8);
	pq.push(5);

	while (!pq.empty())
	{
		cout << pq.top() << " ";
		pq.pop();
	}
	//8 5 3 2 1

显示调用,建小堆。

	qq::priority_queue<int,vector<int>,greater<int>> pq;
	//qq::priority_queue<int> pq;
	pq.push(1);
	pq.push(3);
	pq.push(2);
	pq.push(8);
	pq.push(5);

	while (!pq.empty())
	{
		cout << pq.top() << " ";
		pq.pop();
	}
	//1 2 3 5 8

建立的是大堆(大顶堆),那么每次从堆中取出的都是当前堆中的最大值,再进行pop,向下调整。因此,如果你从大堆中依次取出所有元素并打印,那么打印出来的序列将是降序的。
同理 建立的是小堆(小顶堆),那么每次从堆中取出的都是当前堆中的最小值,再进行pop,向下调整。因此,如果你从小堆中依次取出所有元素并打印,那么打印出来的序列将是升序的。

namespace qq
{
	template<class T>
	class less
	{
	public:
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}
	};

	template<class T>
	class greater
	{
	public:
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};

	template<class T, class Container = std::vector<T>,class Compare = less<T>>
	class priority_queue
	{
	public:
		void adjust_up(size_t child)
		{
			Compare com;
			int parent = (child - 1) / 2;
			while (child > 0)
			{
				if (com(_con[parent], _con[child]))
				{
					std::swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}

		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;
				}
				if (com(_con[parent], _con[child]))
				{
					std::swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}

		void pop()
		{
			std::swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			adjust_down(0);
		}

		size_t size()
		{
			return _con.size();
		}

		bool empty()
		{
			return _con.empty();
		}

		const T& top()
		{ 
			return _con[0];
		}
	private:
		Container _con;
	};
}

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

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

相关文章

基于Echarts的超市销售可视化分析系统(数据+程序+论文

本论文旨在研究Python技术和ECharts可视化技术在超市销售数据分析系统中的应用。本系统通过对超市销售数据进行分析和可视化展示&#xff0c;帮助决策层更好地了解销售情况和趋势&#xff0c;进而做出更有针对性的决策。本系统主要包括数据处理、数据可视化和系统测试三个模块。…

专项1:理论横向误差计算

1.前言 车辆实际位置与轨迹要求的位置的误差大小是反映自动驾驶控制精度的关键性指标&#xff0c;也是作为控制系统的输入量。在对车辆的控制算法进行研究时候&#xff0c;首先需要厘清控制系统的输入。控制系统的输入的关键性环节就是笛卡尔坐标系和frent坐标系之间的转换。 …

【进阶篇】四、字节码增强框架:ASM、ByteBuddy

文章目录 1、ASM2、ASM字节码增强3、ASM入门案例4、ASM Java Agent实现增强类的方法5、Byte Buddy6、Byte Buddy案例 相比自己的代码里用Spring AOP添加某些功能&#xff0c;字节码增强更适配无侵入式的Java Agent场景。比如下面写个Java Agent打印 任意Java程序中方法执行的…

电商技术揭秘九:搜索引擎中的SEO数据分析与效果评估

相关系列文章 电商技术揭秘一&#xff1a;电商架构设计与核心技术 电商技术揭秘二&#xff1a;电商平台推荐系统的实现与优化 电商技术揭秘三&#xff1a;电商平台的支付与结算系统 电商技术揭秘四&#xff1a;电商平台的物流管理系统 电商技术揭秘五&#xff1a;电商平台的个性…

DC-3渗透测试复现

DC-3渗透测试复现 目的&#xff1a; 获取最高权限以及5个flag 过程&#xff1a; 信息打点-sql注入-反弹shell- pkexec提权&#xff08;CVE-2021-4034&#xff09; 环境&#xff1a; 攻击机&#xff1a;kali(192.168.85.136) 靶机&#xff1a;DC_3(192.168.85.133) 复现…

记录一下hive跑spark的insert,update语句报类找不到的问题

我hive能正常启动&#xff0c;建表没问题&#xff0c;我建了一个student表&#xff0c;没问题&#xff0c;但执行了下面一条insert语句后报如下错误&#xff1a; hive (default)> insert into table student values(1,abc); Query ID atguigu_20240417184003_f9d459d7-199…

「每日跟读」英语常用句型公式 第13篇

「每日跟读」英语常用句型公式 第13篇 1. How was __? __怎么样&#xff1f; How was the concert last night? &#xff08;昨晚的音乐会怎么样&#xff1f;&#xff09; How was your trip to the museum? &#xff08;你去博物馆的旅行怎么样&#xff1f;&#xff09…

Rust腐蚀服务器修改背景和logo图片操作方法

Rust腐蚀服务器修改背景和logo图片操作方法 大家好我是艾西一个做服务器租用的网络架构师。在我们自己搭建的rust服务器游戏设定以及玩法都是完全按照自己的想法设定的&#xff0c;如果你是一个社区服那么对于进游戏的主页以及Logo肯定会有自己的想法。这个东西可以理解为做一…

嵌入式4-16

tftpd #include <myhead.h> #define SER_IP "192.168.125.243" //服务器IP地址 #define SER_PORT 69 //服务器端口号 #define CLI_IP "192.168.125.244" //客户端IP地址 #define CLI_PORT 8889 //客户端端…

MSQL DML数据操作语言

&#x1f339;作者主页&#xff1a;青花锁 &#x1f339;简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java微服务架构公号作者&#x1f604; &#x1f339;简历模板、学习资料、面试题库、技术互助 &#x1f339;文末获取联系方式 &#x1f4dd; 往期热门专栏回顾 专栏…

【MogDB】在ORACLE和MogDB中查看存储过程出参游标数据的方式

一、前言 使用ORACLE作为数据库的应用软件中&#xff0c;偶尔会遇到使用游标作为出参的存储过程&#xff0c;这种存储过程迁移到MogDB并不需要进行改造&#xff0c;但是在开发这样的存储过程时&#xff0c;开发人员偶尔会想要在数据库中测试执行一下&#xff0c;看看游标中的数…

Fiddler安装与使用的深度解析

在现今的互联网开发领域&#xff0c;无论是前端开发、后端开发&#xff0c;还是移动应用开发&#xff0c;对HTTP协议的深入理解和应用都至关重要。而在这个过程中&#xff0c;一个强大的HTTP调试代理工具就显得尤为关键。Fiddler&#xff0c;作为一款功能强大的网络调试工具&am…

数据库练习(二)

建表 create table employee(empno int primary key auto_increment , ename char(10) , job char(6) , mgr int , hiredate date , sal float(10,2),comm float(10,2),deptno int ); insert into employee(empno…

【简单介绍下单片机】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

元类的执行

class MetaB(type):def __new__(cls, name, bases, attrs):print(f"使用元类 {cls.__name__} 创建{name}类 ")return super().__new__(cls, name, bases, attrs)class A(metaclassMetaB):passclass C(A):pass元类MetaB的__new__方法应该只会在创建类A时被调用一次, 因…

集成电路测试学习

集成电路&#xff08;Integrated Circuit&#xff0c;IC&#xff09;整个设计流程包括&#xff1a;电路设计、晶圆制造、晶圆测试、IC封装、封装后测试。 IC测试目的&#xff1a;一、确认芯片是否满足产品手册上定义的规范&#xff1b;二、通过测试测量&#xff0c;确认芯片可以…

Python爬虫:requests模块的基本使用

学习目标&#xff1a; 了解 requests模块的介绍掌握 requests的基本使用掌握 response常见的属性掌握 requests.text和content的区别掌握 解决网页的解码问题掌握 requests模块发送带headers的请求掌握 requests模块发送带参数的get请求 1 为什么要重点学习requests模块&…

Unity架构师进阶:红点系统的架构与设计

面试的时候经常被问道如何来设计一个红点系统,本文将详细地介绍如何设计一个红点系统&#xff0c;有哪些接口&#xff0c;并完整地给出实现。 红点系统的需求分析 首先我们来分析一下红点系统的设计需求: 红点系统严格意义上来说不属于框架&#xff0c;而是游戏逻辑&#xff0…

DOS时代经典软件,落下帷幕,国产中文编程,蓬勃发展

互联网的变迁好像翻涌的波涛&#xff0c;有些我们以为已经忘掉的软件&#xff0c;其实还留在我们心里&#xff0c;特别是那些经历过从DOS系统换到Windows系统的人&#xff0c;这种感觉更加明显。 说起DOS软件&#xff0c;它是很多80后年轻时的美好记忆。虽然现在它已经成为了过…

Python数学建模学习-PageRank算法

1-基本概念 PageRank算法是由Google创始人Larry Page在斯坦福大学时提出&#xff0c;又称PR&#xff0c;佩奇排名。主要针对网页进行排名&#xff0c;计算网站的重要性&#xff0c;优化搜索引擎的搜索结果。PR值是表示其重要性的因子。 中心思想&#xff1a; 数量假设&#…