priority_queue模拟实现【C++】

文章目录

  • 全部的实现代码放在了文章末尾
  • 什么是适配器模式?
  • 准备工作
    • 包含头文件
    • 定义命名空间
    • 类的成员变量
    • 什么是仿函数?
    • 比较仿函数在priority_queue中的作用
      • 通过传入不同的仿函数可以做到大堆和小堆之间的切换
      • 通过传入不同的仿函数可以做到改变priority_queue里面的比较逻辑
    • 构造函数
      • 默认构造
      • 迭代器区间构造
    • size
    • empty
    • top
    • push
    • pop
  • 全部代码

全部的实现代码放在了文章末尾

【不了解堆(priority_queue)的可以看我这篇文章:模拟实现堆的接口函数】

priority_queue的模拟实现和stack,queue一样,采用了C++适配器模式

priority_queue的适配器一般是vector,也可以是deque

因为priority_queue是有特殊限制的线性表【只能取堆顶数据,并且需要高效的尾插尾删,还要支持高效的下标访问因为priority_queue的push和pop一般是调用适配器的尾插,尾删。 实现向下(上)调整算法的时候需要高效的下标访问方法)】
所以只要是线性结构并且可以高效的实现尾插和尾删,支持高效的下标访问的线性表,就可以作为priority_queue的适配器


什么是适配器模式?

适配器模式是一种设计模式,它允许将不兼容接口的类一起工作

适配器模式通常用于以下情况:

  1. 希望使用一个类,但其接口与其他代码不兼容。
  2. 希望创建一个可重用的类,它能够将接口转换为其他接口。
  3. 希望使用第三方库或遗留代码,但其接口与其他代码不兼容。

适配器模式通常包括以下三个主要部分:

  1. 目标接口(Target):这是期望使用的接口,客户端代码只能与目标接口交互。
  2. 源接口(Adaptee):这是需要适配的类,其接口与目标接口不兼容。
  3. 适配器(Adapter):这是一个类,它实现了目标接口,并将调用转换为对源接口的调用。适配器将源接口的调用转换为目标接口的调用,使得客户端代码可以与目标接口交互。

可以类比我们生活中的家庭电源接口笔记本电脑充电口电源适配器,它们之间也是一种适配器关系

笔记本电脑充电口是上面提到的目标接口
家庭电源接口是上面提到的源接口
电源适配器是上面提到的适配器

笔记本电脑的充电口是不能和家庭电源接口直接连接进行充电的,因为笔记本电脑用的是直流电,而家庭电源输出的是交流电,所以要把交流电转换为直流电才能给笔记本电脑供电,而电源适配器就能做到这一点

对应了上面提到的适配器模式解决的问题:
可以将不兼容接口的类一起工作


准备工作

创建两个文件,一个头文件mypriority_queue.hpp,一个源文件test.cpp

【因为模板的声明和定义不能分处于不同的文件中,所以把成员函数的声明和定义放在了同一个文件mypriority_queue.hpp中】

  1. mypriority_queue.hpp:存放包含的头文件,命名空间的定义,成员函数和命名空间中的函数的定义

  2. test.cpp:存放main函数,以及测试代码


包含头文件

  1. iostream:用于输入输出
  2. vector:提供vector类型的适配对象
  3. deque: 提供deque类型的适配对象
  4. assert.h:提供assert报错函数

定义命名空间

在文件mypriority_queue.hpp中定义上一个命名空间mypriority_queue
把priority_queue类和它的成员函数放进命名空间封装起来,防止与包含的头文件中的函数/变量重名的冲突问题


类的成员变量

有两个一个是适配器对象,一个是提供比较的仿函数,默认con是vector类型,comp是std库里面的less
在这里插入图片描述


什么是仿函数?

C++中的仿函数是指那些能够像函数一样调用的对象,它们的类至少需要有成员函数operator()()
仿函数可以是任何类型的对象,只要它的类重载了运算符(),它就能够被调用,就可以作为函数使用。
在C++中,标准模板库(STL)中的一些容器算法使用仿函数来封装各种操作,使得这些操作可以应用于容器中的元素。

仿函数可以是用户自定义的,也可以是C++标准库中定义的。例如,STL中的std::lessstd::greater就是两个标准的比较仿函数,它们用于排序算法中。用户也可以定义自己的仿函数

总结
C++的仿函数是重载了operator()的类实例化的对象,它可以被像函数那样调用

举个例子:
下图是一个可以简单进行大于比较的仿函数和它的类
在这里插入图片描述


比较仿函数在priority_queue中的作用

通过传入不同的仿函数可以做到大堆和小堆之间的切换

【stl库里面是传入less类型的仿函数为大堆,传入greater为小堆。 即大于是小堆,小于是大堆
为什么呢?
因为priority_queue需要用到比较的地方是向上(下)调整算法
如下图:
【向上(调整)这篇文章不做详细讲解,不了解的可以看我这篇文章:模拟实现堆的接口函数】
在这里插入图片描述

在这里插入图片描述
根据传给priorit_queue的第三个模板参数的不同,就可以得到不同类型的comp
这样调用comp实现的功能就不一样
std::less类型的comp的功能是判断左参数是否小于右参数
std::greater类型的comp的功能是判断左参数是否大于右参数
通过这一点就可以调整大小堆了
因为
如果comp是小于,那么comp(con[parent],con[child])就是父亲节点<孩子节点就交换,那么就会把大的节点一直往堆顶送,就可以实现大堆
comp是小于的时候同理


通过传入不同的仿函数可以做到改变priority_queue里面的比较逻辑

我们使用priority_queue的时候,一般不需要我们传入我们自己写的仿函数,使用库里面的std::lessstd::greater就可以满足我们的大部分需求

但是总规有一些情况,库里面的比较仿函数的比较逻辑不符合我们的要求
此时就需要我们自己写一个比较仿函数,传给priority_queue


如果我们定义了一个坐标类pos
在这里插入图片描述

假设我们比较两个pos对象的大小的时候分两种情况

  1. 优先比x,谁的x大谁就大,x相等时y大就大
  2. 优先比y,谁的y大谁就大,y相等时x大就大

如果我们用这两种比较方式建出来的堆(priority_queue)肯定是不同的

这个时候库里面的仿函数就满足不了我们的要求了

我们要自己写仿函数:
在这里插入图片描述

此时传入不同的仿函数,得到的堆就不同

在这里插入图片描述

在这里插入图片描述


构造函数

默认构造

由于默认构造可以直接传入适配器对象,因为传入的适配器对象里面可能已经有数据了,所以要把这些数据给建成堆,就算传入的适配器对象是空的也能处理
在这里插入图片描述


在这里插入图片描述


迭代器区间构造

在这里插入图片描述


在这里插入图片描述


size

因为把priority_queue的数据都存储在了适配器对象里面所以适配器对象的size,就是priority_queue的size加const是为了让const修饰的对象也能调用size_t size()const
{return _con.size();
}

empty

因为把priority_queue的数据都存储在了适配器对象里面所以判断适配器对象是否为空即可
加const是为了让const修饰的对象也能调用bool empty() const
{return con.empty();
}

top

堆顶的数据只支持读取,  不支持  修改
因为改了就有可能不是 堆 了
所以返回值是const T&const T& top() const
{因为把priority_queue的数据都存储在了适配器对象里面而且堆顶的数据存储在适配器对象的第一个数据位置所以直接调用front即可return con.front();
}

push

void push(const T& val)
{因为要把priority_queue的数据都存储在适配器对象里面所以新数据,直接尾插到适配器对象里面存储con.push_back(val);再堆新插入的数据调用向上调整算法保持插入之后也还是  堆  结构AdiuUp(size() - 1);
}

pop

void pop()
{堆不为空才能删除assert(!empty());把堆顶与适配器最后一个数据交换方便尾删std::swap(con[0], con[size() - 1]);尾删,把原来的堆顶数据删除con.pop_back();对换到堆顶的数据进行向下调整算法保持删除之后也还是  堆  结构AdiuDown(0);
}

全部代码

#include<iostream>
#include<vector>
#include<deque>
#include<assert.h>
using namespace std;namespace mypriority_queue
{template<class T, class Container = vector<T>, class Compare = less<T>>class priority_queue{private://拥有比较大小能力的仿函数Compare comp;//适配器对象Container con;//向上调整算法void AdiuUp(int child){//找到  逻辑结构  中的父亲节点int parent = (child - 1) / 2;while (child > 0){//使用  比较仿函数comp  进行比较大小//默认的comp是 std::less 类型的//即  左参数<右参数  就返回true,否则返回falseif (comp(con[parent],con[child])){std::swap(con[parent], con[child]);child = parent;parent = (child - 1) / 2;}else{break;}}}//向下调整算法void AdiuDown(int parent){int n = this->size();//找到  逻辑结构  中的左孩子节点int child = parent*2+1;while (child <n){if (child + 1 < n && comp(con[child], con[child + 1])){child++;}//使用  比较仿函数comp  进行比较大小//默认的comp是 std::less 类型的if (comp(con[parent], con[child])){std::swap(con[parent], con[child]);parent = child;child = parent * 2 + 1;}else{break;}}}public:priority_queue(const Container & ctnr = Container(),const Compare & comp = Compare()){this->comp = comp;this->con = ctnr;int n = con.size();//建堆,从最后一个叶子节点的父亲节点开始//n-1为逻辑结构上的  最后一个叶子节点的下标 //它的父亲节点的下标等于  它的下标-1,再除以2for (int i = (n - 1 - 1) / 2; i >= 0; i--){//向下调整算法AdiuDown(i);}}template <class InputIterator>priority_queue(InputIterator first, InputIterator last){//遍历迭代器区间while (first != last){//复用push进行插入push(*first);++first;}}size_t size()const{return con.size();}bool empty() const{return con.empty();}//堆顶的数据只支持读取,  不支持  修改//因为改了就有可能不是 堆 了//所以返回值是const T&const T& top() const{//堆顶的数据存储在适配器对象的第一个数据位置return con.front();}void push(const T& val){//因为要把priority_queue的数据都存储在适配器对象里面//所以新数据,直接尾插到适配器对象里面存储con.push_back(val);//再堆新插入的数据调用向上调整算法//保持插入之后也还是  堆  结构AdiuUp(size() - 1);}void pop(){//堆不为空才能删除assert(!empty());//把堆顶与适配器最后一个数据交换//方便尾删std::swap(con[0], con[size() - 1]);//尾删,把原来的堆顶数据删除con.pop_back();//对换到堆顶的数据进行向下调整算法//保持删除之后也还是  堆  结构AdiuDown(0);}};
}

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

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

相关文章

书生.浦江大模型实战训练营——(三)Git基本操作与分支管理

最近在学习书生.浦江大模型实战训练营&#xff0c;所有课程都免费&#xff0c;以关卡的形式学习&#xff0c;也比较有意思&#xff0c;提供免费的算力实战&#xff0c;真的很不错&#xff08;无广&#xff09;&#xff01;欢迎大家一起学习&#xff0c;打开LLM探索大门&#xf…

Java设计模式(命令模式)

定义 将一个请求封装为一个对象&#xff0c;从而让你可以用不同的请求对客户进行参数化&#xff0c;对请求排队或者记录请求日志&#xff0c;以及支持可撤销的操作。 角色 抽象命令类&#xff08;Command&#xff09;&#xff1a;声明用于执行请求的execute方法&#xff0c;通…

LeNet5模型搭建

文章目录 LeNet1 搭建模型2 训练模型3 测试模型3.1 预测一3.2 预测二 LeNet LeNet 诞生于 1994 年&#xff0c;是最早的卷积神经网络之一&#xff0c;并且推动了深度学习领域的发展。自从 1988 年开始&#xff0c;在许多次成功的迭代后&#xff0c;这项由 Yann LeCun 完成的开拓…

【最长递增子序列】python刷题记录

R4-dp 目录 常规方法遇到以下序列时就会变得错误 动态规划的思路 单调栈 ps: class Solution:def lengthOfLIS(self, nums: List[int]) -> int:#最简单的方法nlen(nums)if n<2:return nmx1for i in range(n):max_i1for j in range(i1,n):if nums[i]<nums[j]:nums…

RK3568平台(触摸篇)FT5X06驱动程序分析

一.设备树 &i2c1 {status "okay";myft5x06: my-ft5x0638 {compatible "my-ft5x06";reg <0x38>;reset-gpios <&gpio0 RK_PB6 GPIO_ACTIVE_LOW>;interrupt-parent <&gpio3>;interrupts-gpio <&gpio3 RK_PA5 GPI…

大数据-70 Kafka 高级特性 物理存储 日志存储 日志清理: 日志删除与日志压缩

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

K8S资源之NameSpace

作用 隔离资源(默认不隔离网络) 查看所有的NS kubectl get ns创建NS kubectl create ns hello删除NS kubectl delete ns hello

VUE基础快速入门

VUE 和 VUE-Cli VUE 是一种流行的渐进式JavaScript框架&#xff0c;用于构建Web用户界面它具有易学、轻量级、灵活性强、高效率等特点&#xff0c;并且可以与其他库和项目集成是目前最流行的前端框架之一VUE-Cli 称为“VUE脚手架”,它是由VUE官方提供的客户端&#xff0c;专门为…

简单Qt贪吃蛇项目

目录 先看效果 项目介绍 界面一&#xff1a;游戏大厅界面 界面二&#xff1a;关卡选择界面​编辑 界面三&#xff1a;游戏界面 游戏大厅页面 游戏关卡选择页面 游戏房间页面 封装贪吃蛇数据结构 初始化游戏房间界面 设置窗口大小、标题、图标等 蛇的移动 初始化贪…

RocketMQ Dashboard安装

RocketMQ Dashboard 是一个基于 Web 的管理工具&#xff0c;用于监控和管理 RocketMQ 集群。它提供了一个用户友好的界面&#xff0c;使管理员能够轻松地查看和操作 RocketMQ 系统中的各种组件和状态。 主要功能包括&#xff1a; 集群管理: 监控和管理 NameServer 和 Broker …

大数据-65 Kafka 高级特性 分区 Broker自动再平衡 ISR 副本 宕机恢复再重平衡 实测

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

【vulnhub】W34kn3ss 1靶机

安装靶机 下载地址&#xff1a;https://www.vulnhub.com/entry/w34kn3ss-1,270/# 信息收集 靶机扫描 nmap 192.168.93.0/24 打开端口为22、80、443 网址访问 目录扫描 dirsearch -u http://192.168.93.162 在网址后面拼接扫到的目录&#xff0c;在/test目录下发现信息 提…

微型导轨:光学仪器精准定位的支撑者

微型导轨是指宽度在25mm以下的导轨系统&#xff0c;通常由导轨和滑块组成&#xff0c;具有体积小、重量轻、精度高、噪音低、寿命长等特点。主要用于支撑和定位光学元件&#xff0c;如镜子、透镜、滤光片等。微型导轨通过提供高精度的运动控制&#xff0c;‌有利于提高设备的性…

【Tessent IJATG Users Manual】【Ch5】IJTAG Network Insertion

The IJTAG Network Insertion FlowIJTAG Network Insertion ExampleModification of the IJTAG Network Insertion Flow How to Edit or Modify a DftSpecificationEdit or Modify MethodDftSpecification Examples IJTAG Network Insertion 可以将已有的 instrument 连接起来&…

Docker学习(6):Docker Compose部署案例

一、docker-compose部署mysql 1、准备镜像 2、编写my.cnf配置文件 # 服务端参数配置 [mysqld] usermysql # MySQL启动用户 default-storage-engineINNODB # 创建新表时将使用的默认存储引擎 character-set-serverutf8mb4 # 设置mysql服务端默认字符集…

离线+树状数组,ABC253 F - Operations on a Matrix

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 F - Operations on a Matrix 二、解题报告 1、思路分析 我们通过差分树状数组&#xff0c;可以轻松解决操作1 操作3我们也可以通过树状数组来获取对应列的值 关键是操作2会对操作3造成影响 所以我们先对…

【Linux】yum软件包管理器(使用、生态、yum源切换)

目录 1.yum-软件包管理器&#x1f638;1.1yum使用方法1.2什么是yum&#xff1f;&#x1f638;1.3yum的周边生态1.4yum源切换1.4.1 查看系统本身yum源1.4.2 软件源1.4.3yum源配置 1.yum-软件包管理器 以下操作需要联网的情况下进行 &#x1f638;1.1yum使用方法 安装软件时由于需…

【学习笔记】Day 7

一、进度概述 1、DL-FWI基础入门培训笔记 2、inversionnet_train 试运行——未成功 二、详情 1、InversionNet: 深度学习实现的反演 InversionNet构建了一个具有编码器-解码器结构的卷积神经网络&#xff0c;以模拟地震数据与地下速度结构的对应关系。 &#xff08;一…

03 库的操作

目录 创建查看修改删除备份和恢复查看连接情况 1. 创建 语法 CRATE DATABASE [IF NOT EXISTS] db_name [create_specification [, create_specification] …] create_specification:  CHARACTER SET charset_name  CPLLATE collation_name 说明&#xff1a; 大写的标识关键…