【落羽的落羽 C++】stack和queue、deque、priority_queue、仿函数

文章目录

  • 一、stack和queue
    • 1. 概述
    • 2. 使用
    • 3. 模拟实现
  • 二、deque
  • 三、priority_queue
    • 1. 概述和使用
    • 2. 模拟实现
  • 四、仿函数

一、stack和queue

1. 概述

我们之前学习的vector和list,以及下面要认识的deque,都属于STL的容器(containers)组件。而stack和queue,属于STL的配置器(或称为配接器)(adapters)组件,或者归类为容器配置器(container adapters),它们可以修饰容器的接口而呈现出全新的容器性质,即stack的“先进后出”和queue的“先进先出”特点。

2. 使用

stack的所有元素的进出都必须符合“先进后出”的条件,queue的所有元素的进出都必须符合“先进先出”的条件。换句话说,只有stack的栈顶元素和queue的队头元素才有机会被移除,因此stack和queue不提供遍历的功能,也不提供迭代器。

除此之外,stack和queue的功能和使用也都很好理解了,之前我们已经学过栈和队列。

  • stack:
函数声明接口说明
stack()构造空的栈
empty()检测栈是否为空
size()返回栈中元素个数
top()返回栈顶元素的引用
push()在栈顶入栈
pop()将栈顶元素出栈

使用演示:
在这里插入图片描述

  • queue:
函数声明接口说明
queue()构造空的队列
empty()检测队列是否为空
size()返回队列中元素个数
front()返回队头元素的引用
back()返回队尾元素的引用
push()在队尾入队列
pop()将队头元素出队列

使用演示:
在这里插入图片描述

3. 模拟实现

STL库中的stack和queue的模板实际上有两个模板类型:
在这里插入图片描述
在这里插入图片描述
第一个就是要存储的数据类型了。第二个代表它们所修饰的容器类型,前面说过,它们不是独立的容器,而是容器配置器,要依靠别的容器才能实现它们,这就是第二个模板参数的意义。我们可以用vector或list来实现出stack和queue,如stack<int, vector<int>>queue<char, list<char>>,我们在上层使用stack和queue时是感受不到vector或list的区别的。不论是哪种容器,都有push、pop、front、back、empty等相关操作,得以再封装成stack和queue的功能。
模板参数也是可以有缺省值的,我们能看到STL标准库中的stack和queue的Container模板类型默认给了deque,deque也是一种容器,我们一会再介绍它。

除了这一点,stack和queue的模拟实现就很简单了,遵循它们的特性就好:

namespace lydly
{template<class T, class Container = deque<T>>class queue{private:Container _con;public:void push(const T& x){_con.push_back(x);}void pop(){_con.pop_front();}T& front(){return _con.front();}const T& front() const{return _con.front();}T& back(){return _con.back();}const T& back() const{return _con.back();}bool empty() const{return _con.empty();}size_t size() const{return _con.size();}};template<class T, class Container = deque<T>>class stack{private:Container _con;public:void push(const T& x){_con.push_back(x);}void pop(){_con.pop_back();}T& top(){return _con.back();}const T& top() const{return _con.back();}bool empty() const{return _con.empty();}size_t size() const{return _con.size();}};
}

二、deque

关于deque,简单了解就好。
对比一下vector和list的优缺点:

容器优点缺点
vector支持下标随机访问、CPU高速缓存命中率高头部或中部插入删除数据效率低、扩容有一定成本,存在一定浪费
list任意位置插入删除数据效率高、不用扩容,按需申请空间,不存在浪费不支持下标随机访问、CPU高速缓存命中率低

可见,vector和queue都有各自的优缺点,deque包含了两种容器的优点,是一种双向开口的线性连续空间

在这里插入图片描述
deque有一个中控器,存放着指向不同节点的指针,每一个节点是一个缓冲区buffer,是一个数组用于存放数据。执行尾插时,就在当前buffer的尾部插入数据,若当前buffer已满,就去中控器的下一个节点指向的buffer的第一个位置存放;执行头插时,要倒着看,当前buffer头部没有空间,就去中控器的上一个节点指向的buffer的最后一个位置存放,再头插一次就存放到倒数第二个位置,以此类推。头删和尾删也是一样的道理。

在这里插入图片描述
deque并不是真正完全连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似一个动态的二维数组,deque的迭代器的结构就更为复杂了。

所以,deque与vector相比,头部插入效率更高,不需要挪动元素;与list相比,空间利用率更高。但是,deque不适合遍历,因为在遍历时迭代器需要频繁检测每一个buffer是否到达边界,导致效率低下。而在序列式场景中,可能经常需要遍历,所以实际需要线性数据结构时,大多数情况下优先考虑vector和list,deque的应用并不多,目前能看到的一个应用就是STL用它作为stack和queue的底层数据结构。
而STL选择deque作为stack和queue的底层,主要原因也是stack和queue不需要遍历,只在固定的一端或两端进行操作,以及deque结合vector和list的优点了。

三、priority_queue

1. 概述和使用

priority_queue,意为优先级队列,是queue的一种,带有权值观念,其内的元素并非按照入队列的顺序排列,而是按照元素的权值排列,默认权值较高的元素排在前面。
举个栗子:
在这里插入图片描述
有没有发现,优先级队列其实就是我们之前学过的堆。
在这里插入图片描述
优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的场景,都可以考虑使用优先级队列。默认情况下,优先级队列是大堆。

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

2. 模拟实现

既然优先级队列就是堆,那么实现优先级队列也就和实现堆道理类似了:
传送门:https://blog.csdn.net/2402_86681376/article/details/145808942?spm=1011.2415.3001.5331(堆的讲解
要用到的向上调整算法、向下调整算法,我们都学习过了。

namespace lydly
{template<class T, class Container = vector<T>>class priority_queue{private:Container _con;public:priority_queue(){}void push(const T& x){_con.push_back(x);adjustup(_con.size() - 1);}void pop(){swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjustdown(0);}const T& top() const{return _con[0];}bool empty() const{return _con.empty();}size_t size() const{return _con.size();}private:void adjustup(int child){int parent = (child - 1) / 2;while (child > 0){if (_con[parent] < _con[child]){swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}}void adjustdown(size_t parent){size_t child = parent * 2 + 1;while (child < _con.size()){if (child + 1 < _con.size() && _con[child] < _con[child + 1]){++child;}if (_con[parent] < _con[child]){swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else{break;}}}};
}

问题来了,我们想让优先级队列降序排列元素,再重载一次·未必太麻烦了,怎么办呢?这里就需要用到仿函数。

四、仿函数

在STL标准库中,我们看到优先级队列的模板参数中还有一个Compare,这个就是仿函数。
在这里插入图片描述
仿函数是一种类对象,顾名思义,它可以“模仿函数”,允许用户“以模板参数来指定所要采取的策略”。在priority_queue中,它的第三个模板类型参数就是仿函数,默认给了一个less,less其实是这样的:

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

less是一个类,less<typename Container::value_type>就是一个匿名对象,它的里面重载了(),这样一来,倘若有less<T> Com;那我们就可以写出Com(a, b),(a,b是T类型的)这个表达式的意思就是a < b
举一反三,STL中还有greater的仿函数:

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

倘若有greater<T> Com;那我们就可以写出Com(a, b),这个表达式的意思就是a > b

大堆和小堆的区别在于AdjustUp和AdjustDown中几处<或>的不同,有了上面的两种仿函数,我们就能在创建优先级队列时根据需要,模板参数传less或greater,区分大堆和小堆。具体是<还是>,就可以依靠传的是less还是greater来分别。STL的仿函数中的less和greater,就可以传给priority_queue的模板:
在这里插入图片描述

我们的模拟实现也按照这个思路来完成:

namespace lydly
{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 = vector<T>, class Compare = less<T>>class priority_queue{private:Container _con;public:priority_queue(){}void push(const T& x){_con.push_back(x);adjustup(_con.size() - 1);}void pop(){swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjustdown(0);}const T& top() const{return _con[0];}bool empty() const{return _con.empty();}size_t size() const{return _con.size();}private:void adjustup(int child){//构造一个less或greater的对象Compare com;int parent = (child - 1) / 2;while (child > 0){if (com(_con[parent], _con[child])){swap(_con[child], _con[parent]);// child = parent;parent = (child - 1) / 2;}else{break;}}}void adjustdown(size_t parent){//构造一个less或greater的对象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])){swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else{break;}}}};
}

关于less,我们还可能会遇到其他情况:

  • 当想要比较的是自定义类型时,就需要这个自定义类型中重载<和>运算符,这一点很好理解。
  • 当传入指针时,一般我们应该是想要比较的是指向的内容,但如果只把less写成刚才那样,会被解析成直接比较两个地址的值。这时,需要写成这样:
template<class T>
class less<T*>
{
public:bool operator()(const T* const & x, const T* const & y){return *x < *y;}
};

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

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

相关文章

用生活例子通俗理解 Python OOP 四大特性

让我们用最生活化的方式&#xff0c;结合Python代码&#xff0c;来理解面向对象编程的四大特性。 1. 封装&#xff1a;像使用自动售货机 生活比喻&#xff1a; 你只需要投币、按按钮&#xff0c;就能拿到饮料 不需要知道机器内部如何计算找零、如何运送饮料 如果直接打开机…

软件安全(三)实现后门程序

如下是一个经典的后门程序 #define _WINSOCK_DEPRECATED_NO_WARNINGS 1 #include<WinSock2.h> #include<windows.h> #include<iostream> #pragma comment(lib, "ws2_32.lib")int main() {//初始化网络环境WSADATA wsaData;int result WSAStartup…

深入理解高性能网络通信:从内核源码到云原生实践

深入理解高性能网络通信&#xff1a;从内核源码到云原生实践 前言 随着互联网业务规模的高速增长&#xff0c;服务端网络通信能力成为系统性能的核心瓶颈。如何支撑百万级连接、在极限场景下实现低延迟高吞吐&#xff1f;本篇博客将围绕Linux通信机制内核剖析、性能调优实战、…

从实战看软件测试与质量管理:方法、过程与质量的全景解读

作为一名高级软件测试工程师&#xff0c;在过往多个大型系统项目的测试工作中&#xff0c;我深刻体会到&#xff1a;软件测试不仅是产品质量的“守门员”&#xff0c;更是项目成功的“加速器”。今天这篇文章&#xff0c;我将站在实战角度&#xff0c;结合具体案例&#xff0c;…

Megatron系列——流水线并行

内容总结自&#xff1a;bilibili zomi 视频大模型流水线并行 注&#xff1a;这里PipeDream 1F1B对应时PP&#xff0c;Interleaved 1F1B对应的是VPP 1、朴素流水线并行 备注&#xff1a; &#xff08;1&#xff09;红色三个圈都为空泡时间&#xff0c;GPU没有做任何计算 &am…

在Web应用中集成Google AI NLP服务的完整指南:从Dialogflow配置到高并发优化

在当今数字化客服领域,自然语言处理(NLP)技术已成为提升用户体验的关键。Google AI提供了一系列强大的NLP服务,特别是Dialogflow,能够帮助开发者构建智能对话系统。本文将详细介绍如何在Web应用中集成这些服务,解决从模型训练到高并发处理的全套技术挑战。 一、Dialogflow…

Wi-Fi网络角色及功能详解

在 Wi-Fi 网络中&#xff0c;不同的角色和组件协同工作以实现无线通信。以下是 Wi-Fi 中的主要角色及其功能&#xff1a; 1. 基础设施模式&#xff08;Infrastructure Mode&#xff09; 这是最常见的 Wi-Fi 网络架构&#xff0c;包含以下核心角色&#xff1a; 接入点&#xff…

密码学--希尔密码

一、实验目的 1、通过实现简单的古典密码算法&#xff0c;理解密码学的相关概念 2、理解明文、密文、加密密钥、解密密钥、加密算法、解密算法、流密码与分组密码等。 二、实验内容 1、题目内容描述 ①定义分组字符长度 ②随机生成加密密钥&#xff0c;并验证密钥的可行性 …

[C++] 一个线程打印奇数一个线程打印偶数

要求开辟两个线程打印从0-100的数&#xff0c;一个线程打印奇数一个线程打印偶数&#xff0c;要求必须按照1,2,3,4,5,6…100这种按照顺序打印 使用std::shared_mutex的版本 #ifndef PrintNumber2_H_ #define PrintNumber2_H_#include <shared_mutex>class PrintNumber2…

MySQL全量、增量备份与恢复

目录 数据备份 一、数据备份类型 二、常见备份方法 扩展&#xff1a;GTID与XtraBackup ‌一、GTID&#xff08;全局事务标识符&#xff09;‌ ‌1. 定义与核心作用‌ ‌2. GTID在备份恢复中的意义‌ ‌3. GTID配置与启用‌ ‌二、XtraBackup的意义与核心价值‌ ‌1. 定…

木马查杀篇—Opcode提取

【前言】 介绍Opcode的提取方法&#xff0c;并探讨多种机器学习算法在Webshell检测中的应用&#xff0c;理解如何在实际项目中应用Opcode进行高效的Webshell检测。 Ⅰ 基本概念 Opcode&#xff1a;计算机指令的一部分&#xff0c;也叫字节码&#xff0c;一个php文件可以抽取出…

DeepSeek-R1-Distill-Qwen-1.5B代表什么含义?

DeepSeek‑R1‑Distill‑Qwen‑1.5B 完整释义与合规须知 一句话先行 这是 DeepSeek‑AI 把自家 R1 大模型 的知识&#xff0c;通过蒸馏压缩进一套 Qwen‑1.5B 架构 的轻量学生网络&#xff0c;并以宽松开源许可证发布的模型权重。 1 | 名字逐段拆解 片段意义备注DeepSee…

Megatron系列——张量并行

本文整理自bilibili Zomi视频 1、行切分和列切分 注意&#xff1a; &#xff08;1&#xff09;A按列切分时&#xff0c;X无需切分&#xff0c;split复制广播到A1和A2对应设备即可。最后Y1和Y2需要拼接下&#xff0c;即All Gather &#xff08;2&#xff09;A按行切分时&#…

java agent技术

从JDK1.5之后引入了java angent技术 Java Agent 是一种强大的技术&#xff0c;它允许开发者在 JVM 启动时或运行期间动态地修改类的字节码&#xff0c;从而实现诸如性能监控、日志记录、AOP&#xff08;面向切面编程&#xff09;等功能 java agent依赖于Instrumentation API&…

LLaMA Factory 深度调参

注意&#xff0c;本文涵盖从基础调参到前沿研究的完整知识体系&#xff0c;建议结合具体业务场景灵活应用。一篇“参考文献”而非“可运行的代码”。https://github.com/zysNLP/quickllm 初始指令&#xff1a; llamafactory-cli train \--stage sft \--do_train True \--mode…

Linux驱动:驱动编译流程了解

要求 1、开发板中的linux的zImage必须是自己编译的 2、内核源码树,其实就是一个经过了配置编译之后的内核源码。 3、nfs挂载的rootfs,主机ubuntu中必须搭建一个nfs服务器。 内核源码树 解压 tar -jxvf x210kernel.tar.bz2 编译 make x210ii_qt_defconfigmakeCan’t use ‘…

Redis集群模式、持久化、过期策略、淘汰策略、缓存穿透雪崩击穿问题

Redis四种模式 单节点模式 架构​​&#xff1a;单个Redis实例运行在单台服务器。 ​​优点​​&#xff1a; ​​简单​​&#xff1a;部署和配置容易&#xff0c;适合开发和测试。 ​​低延迟​​&#xff1a;无网络通信开销。 ​​缺点​​&#xff1a; ​​单点故障​​&…

1.2 函数

函数的本质是描述变量间的依赖关系&#xff1a;​​一个变量&#xff08;自变量&#xff09;的变化会唯一确定另一个变量&#xff08;因变量&#xff09;的值​​。 ​​基本构成​​&#xff1a;通过符号&#xff08;如YF(X)&#xff09;表达规则&#xff0c;X输入 → F处理 …

2025数字孪生技术全景洞察:从工业革命到智慧城市的跨越式发展

引言 数字孪生技术&#xff0c;这一融合物理世界与虚拟镜像的革新性工具&#xff0c;正以惊人的速度重塑产业格局。2025年&#xff0c;中国数字孪生市场规模预计达214亿元&#xff0c;工业制造领域占比超40%&#xff0c;其技术深度与行业落地成果令人瞩目。本文将结合最新数据与…

RabbitMQ 工作模式

RabbitMQ 一共有 7 中工作模式&#xff0c;可以先去官网上了解一下&#xff08;一下截图均来自官网&#xff09;&#xff1a;RabbitMQ 官网 Simple P&#xff1a;生产者&#xff0c;要发送消息的程序&#xff1b;C&#xff1a;消费者&#xff0c;消息的接受者&#xff1b;hell…