突破编程_C++_STL教程( queue 的基础知识)

1 std::queue 概述

std::queue 是 C++ 标准模板库(STL)中的一种容器适配器,它提供了队列(Queue)这种数据结构的功能。队列是一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。因此,队列具有先进先出(FIFO)的特性。

1.1 std::queue 的内部实现

std::queue 的内部实现通常基于其他容器,如 std::deque(双端队列)或 std::list。这种实现方式使得 std::queue 能够提供队列(Queue)这种数据结构的功能,即只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,从而实现先进先出(FIFO)的特性。

(1)基于 std::deque 的实现:

当 std::queue 基于 std::deque 实现时,其内部存储机制利用了 deque 的双向操作特性。deque 允许在其头部和尾部都进行高效的插入和删除操作,这使得它非常适合作为队列的底层容器。
在这种实现中,std::queue 的 push 操作会将元素添加到 deque 的尾部,而 pop 操作会从 deque 的头部删除元素。front 和 back 操作则分别返回 deque 的头部和尾部元素。
由于 deque 提供了直接访问其头部和尾部的接口,因此这种实现方式通常具有较高的性能。

(2)基于 std::list 的实现:

当 std::queue 基于 std::list 实现时,其内部存储机制利用了 list 的双向链表结构。虽然 list 在任意位置进行插入和删除操作都较为高效,但作为队列使用时,主要的操作还是集中在头部和尾部。
在这种实现中,std::queue 的 push 操作会将元素添加到 list 的尾部,而 pop 操作会从 list 的头部删除元素。与基于 deque 的实现类似,front 和 back 操作分别返回 list 的头部和尾部元素。
然而,需要注意的是,虽然 list 允许在任意位置进行插入和删除操作,但在作为队列使用时,其性能可能不如基于 deque 的实现,因为 deque 在头部和尾部的操作通常更加高效。

注意:没有明确指定 std::queue 的底层容器类型时,它默认使用 std::deque

1.2 std::queue 的性能特点

std::queue 的性能特点主要源于其内部使用的容器类型,通常是 std::deque 或 std::list。

(1)基于std::deque的性能特点:

当 std::queue 基于 std::deque 实现时,它通常能够展现出非常优秀的性能。这是因为 std::deque 是一个双向队列,它允许在两端进行高效的插入和删除操作。因此,在 std::queue 中,使用 push 在尾部插入元素和使用 pop 在头部删除元素的操作通常都非常快。

此外,std::deque 通常是以固定大小的块来存储元素,这种存储方式减少了内存分配和释放的次数,从而提高了性能。这也使得 std::queue 在处理大量元素时能够保持稳定的性能。

(2)基于std::list的性能特点:

虽然 std::queue 也可以基于 std::list 实现,但相比基于 std::deque 的实现,其性能可能稍逊一筹。std::list 是一个双向链表,虽然它在任意位置插入和删除元素都比较高效,但相对于 std::deque,它在头部和尾部进行操作的性能可能稍差。

具体来说,std::list 中的元素是分散在内存中的,这可能导致缓存不命中(cache misses)的问题,从而降低性能。此外,由于链表需要维护指针或迭代器来跟踪元素的位置,这也可能增加一些额外的开销。

(3)时间复杂度:

对于 std::queue 的基本操作,如 push、pop、front 和 back,其时间复杂度通常都是常数时间 O(1)。这意味着无论队列中有多少元素,这些操作所需的时间都是固定的。

然而,需要注意的是,虽然这些基本操作的时间复杂度是常数,但在实际应用中,性能还可能受到其他因素的影响,如内存分配、缓存行为以及并发访问等。

(4)实际应用中的考虑:

在选择使用 std::queue 时,通常不需要过多关心其底层实现和性能特点。因为标准库已经提供了优化过的实现,并且在大多数情况下都能够满足性能需求。然而,在一些对性能要求非常高的场景下,可能需要考虑使用更底层的容器或自定义数据结构来替代 std::queue。

2 std::queue 的基本使用

2.1 std::queue 的声明与初始化

声明

首先,需要包含<queue>头文件以使用 std::queue:

#include <queue>  
#include <string>  // 声明一个整数类型的 queue  
std::queue<int> vals;// 声明一个字符串类型 queue  
std::queue<std::string> strs;// 声明一个自定义类型的 queue  
struct MyStruct
{int id;std::string name;
};std::queue<MyStruct> myStructs;

初始化

可以使用多种方法来初始化 std::queue。

(1)默认初始化:

如果不提供任何参数,std::queue 会使用默认构造函数进行初始化。这意味着它会使用其底层容器(默认为 std::deque)的默认构造函数。

std::queue<int> q;

(2)使用 std::deque 进行初始化:

虽然 std::deque 不支持初始化列表,但可以使用以初始化列表初始化的 std::deque<int> 来进行初始化。

std::queue<int> q(std::deque<int>{1, 2, 3, 4, 5});  // 使用 std::deque<int> 初始化队列 q

(3)复制另一个队列:

可以使用另一个 std::queue 的副本来初始化一个新的队列。

std::queue<int> q1(std::deque<int>{1, 2, 3, 4, 5});  
std::queue<int> q2(q1);  // 使用q1的内容初始化q2

(4)移动另一个队列:

C++11 及更高版本还支持移动语义,这意味着可以转移另一个队列的内容来初始化新的队列,而不需要复制元素。

std::queue<int> q1 = {1, 2, 3, 4, 5};  
std::queue<int> q2(std::move(q1));  // 使用 q1 的内容(通过移动)初始化 q2,q1 现在为空

(5)指定底层容器:

虽然不常见,但可以通过指定底层容器来初始化 std::queue。这要求提供一个容器对象,该对象将用作队列的底层存储。

std::list<int> l = {1, 2, 3, 4, 5};  
std::queue<int, std::list<int>> q(l);  // 使用 list l 作为底层容器初始化队列 q

2.2 std::queue 的大小与容量

(1)大小(size)

std::queue 的大小是指队列中当前存储的元素数量。可以使用 std::queue 的 size 成员函数来获取队列的大小。例如:

std::queue<int> q;  
q.push(1);  
q.push(2);  
q.push(3);  std::size_t size = q.size(); // size 现在是 3,因为队列中有 3 个元素

这个例子向队列中添加了三个元素,并使用 size 成员函数获取队列的大小。

(2)容量(capacity)

与 std::vector 或 std::deque 不同,std::queue 没有直接提供获取其“容量”的成员函数。容量通常指的是容器在不进行内存重新分配的情况下可以容纳的元素数量。由于 std::queue 的设计是为了提供队列操作的接口,并且隐藏了其底层容器的实现细节,因此它并不直接暴露底层容器的容量信息。

如果需要了解底层容器的容量信息,可能需要直接操作底层容器,但这通常不是使用 std::queue 的推荐做法,因为它违反了队列的抽象和封装原则。

2.3 std::queue 的构造函数与析构函数

(1)构造函数

std::queue 提供了多个构造函数,以便在不同的情况下灵活地初始化队列。以下是一些主要的构造函数:

默认构造函数:

std::queue<Type> q;

此构造函数创建一个空的队列,其底层容器使用默认构造函数进行初始化。这里的 Type 是队列中元素的类型。

拷贝构造函数:

std::queue<Type> q1(q2);

此构造函数使用另一个队列 q2 的内容来初始化新的队列 q1。它复制 q2 中的所有元素到 q1 中。

移动构造函数(C++11 及更高版本):

std::queue<Type> q1(std::move(q2));

此构造函数通过移动另一个队列 q2 的内容来初始化新的队列 q1。这意味着 q2 在移动操作后不再包含其原始元素,这些元素的所有权现在属于 q1。使用移动构造函数通常比使用拷贝构造函数更高效,因为它可以避免不必要的元素复制。

(2)析构函数

当 std::queue 对象的生命周期结束时,其析构函数会被自动调用。析构函数负责清理队列所占用的资源,包括释放底层容器的内存。注意不需要显式地调用析构函数,因为 C++ 的自动存储期管理会处理这些细节。

例如:

{  std::queue<int> q;  // ... 在这里使用队列 ...  
} // 在这里,当 q 离开其作用域时,其析构函数会自动被调用

在上面的代码中,当 q 离开其作用域时,其析构函数会被自动调用,从而释放队列所占用的资源。

3 std::queue 的元素操作

3.1 入队列操作(push)

入队列操作使用 push 成员函数,它接受一个参数,即要添加到队列顶的元素。例如:

std::queue<int> q;  
q.push(1); // 将整数 1 压入队列中  
q.push(2); // 将整数 2 压入队列中

这个例子创建了一个 int 类型的队列 q,并使用 push 函数将两个整数依次压入队列中。

3.2 出队列操作(pop)

出队列操作使用 pop 成员函数,它移除队列顶的元素,但不返回该元素的值。例如:

std::queue<int> q;  
q.push(1);  
q.push(2);  
q.pop(); // 移除队列顶元素 1,但不返回它

这个例子创建了一个队列 q 并压入两个整数。然后,使用 pop 函数移除了队列头部的元素 1。

3.3 查看队列头部元素(front)

查看队列头部元素使用 front 成员函数,它返回队列头部元素的引用,但不移除该元素。例如:

std::queue<int> q;  
q.push(1);  
q.push(2);  
int frontElement = q.front(); // 获取队列头部元素,此时 frontElement 的值为 1

这个例子创建了一个队列 q 并压入两个整数。然后,使用 front 函数获取了队列头部的元素,并将其值存储在 frontElement 变量中。

3.4 查看队列尾部元素(back)

查看队列尾部元素使用 back 成员函数,它返回队列尾部元素的引用,但不移除该元素。例如:

std::queue<int> q;  
q.push(1);  
q.push(2);  
int backElement = q.back(); // 获取队列尾部元素,此时 backElement 的值为 2

这个例子创建了一个队列 q 并压入两个整数。然后,使用 back 函数获取了队列尾部的元素,并将其值存储在 backElement 变量中。

3.5 检查队列是否为空(empty)

检查队列是否为空使用 empty 成员函数,如果队列为空,则返回 true;否则返回 false。例如:

std::queue<int> q;  
bool isEmpty = q.empty(); // isEmpty 的值为 true,因为队列是空的  
q.push(1);  
isEmpty = q.empty(); // isEmpty 的值为 false,因为队列不再为空

这个例子首先创建了一个空的队列 q,并使用 empty 函数检查其是否为空。然后,压入一个整数并再次检查队列是否为空。

3.6 队列的交换(swap)

可以使用 swap 成员函数来交换两个队列的内容。例如:

std::queue<int> q1, q2;  
q1.push(1);  
q1.push(2);  
q2.push(3);  
q2.push(4);  q1.swap(q2); // 交换 q1 和 q2 的内容

在这个例子中,q1 原本包含元素 1 和 2,q2 包含元素 3 和 4。调用 swap 后,q1 将包含元素 3 和 4,而 q2 将包含元素 1 和 2。

3.6 底层容器的访问

虽然直接访问 std::queue 的底层容器通常是不推荐的(因为它破坏了队列的封装性),但 STL 仍然提供了某种程度的访问能力。可以使用 _Get_container 成员函数来获取底层容器的引用。注意:应该非常小心地使用这个功能,并只在确实需要时才使用它。

std::queue<int> q;  
auto& underlyingDeque = q._Get_container(); // 获取底层 deque 的引用(注意:这通常不是好的做法)

4 std::queue 的删除操作

std::queue 是一个后进先出(FIFO)的数据结构,其设计初衷是提供基本的队列操作,如 push(压入元素)、pop(弹出元素)、top(查看队列顶元素)等。然而,std::queue 并没有直接提供删除队列中特定元素的操作,这是因为它保持了队列的简单性和一致性。

如果需要删除队列中的特定元素,那么可能需要考虑其他的数据结构,如 std::deque 或 std::list,它们提供了更多的元素操作功能。但如果仍然想要使用 std::queue 并删除其中的元素,那么可以通过以下方式间接实现:

(1)弹出元素直到找到并删除目标元素:

可以通过连续调用 pop 函数,直到找到并删除目标元素。但是,这种方法会破坏队列的结构,因为它会移除队列顶的所有元素,直到找到目标元素为止。这通常不是推荐的做法,因为它违反了队列的 FIFO 原则。

std::queue<int> q;
q.push(1);
q.push(2);
q.push(3);
q.push(4);std::queue<int> qTmp;int target = 3;
bool found = false;
while (!q.empty()) {int front = q.front();q.pop();if (front != target) {qTmp.push(front); // 将非目标元素重新压入队列中  }
}q.swap(qTmp);

这个例子试图删除值为 3 的元素。通过循环不断地从队列顶弹出元素,检查它是否是想要删除的目标,如果不是,则将其重新压入备用队列中。这种方法效率很低,特别是当队列很大且目标元素靠近队列底时。

(2)使用其他数据结构辅助:

另一种方法是使用一个辅助的数据结构(如 std::vector 或 std::deque)来存储队列中的元素,然后在这个辅助数据结构中删除目标元素,最后再将辅助数据结构中的元素重新压入队列中。这种方法同样会破坏队列的结构,并且效率也不高。

(3)避免需要删除操作:

最好的方法是避免在 std::queue 中进行删除操作。在设计程序时,尽量确保你不需要从队列中删除特定的元素。如果确实需要这种功能,那么可能需要考虑使用其他更适合的数据结构。

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

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

相关文章

Linux运维_Bash脚本_编译安装Apache(httpd-2.4.54)

Linux运维_Bash脚本_编译安装Apache(httpd-2.4.54) Bash (Bourne Again Shell) 是一个解释器&#xff0c;负责处理 Unix 系统命令行上的命令。它是由 Brian Fox 编写的免费软件&#xff0c;并于 1989 年发布的免费软件&#xff0c;作为 Sh (Bourne Shell) 的替代品。 您可以在…

基于Java中的SSM框架实现在线通用旅游平台网站系统项目【项目源码+论文说明】计算机毕业设计

基于Java中的SSM框架实现在线通用旅游平台网站系统演示 摘要 近几年来&#xff0c;计算机网络的发展得到了飞速的提升&#xff0c;由此展开的一系列行业大洗牌也由此开始。早些年只是人们只是对于计算机和互联网有了些基础的认识&#xff0c;现在它正在悄悄的改变着我们生活的…

富格林:运用可信技巧揪出暗箱黑幕

富格林悉知&#xff0c;在伦敦金中&#xff0c;对于市场中的暗箱黑幕骗局投资者应该从容应对&#xff0c;利用可信技巧顺利盈利。在市场投资中&#xff0c;投资者需要不断的学习伦敦金基础知识&#xff0c;总结可信的做单方法&#xff0c;更要从以往案例分析受害原因正规预防。…

安装MySQL5.7.19 + 解决数据库乱码

文章目录 1.删除mysql服务 sc delete mysql2.解压到D:\mysql5.7下3.配置管理员环境变量4.D:\mysql5.7\mysql-5.7.19-winx64下创建my.ini1.创建文件2.文件内容 5.管理员打开cmd&#xff0c;切换到 D:\mysql5.7\mysql-5.7.19-winx64\bin6.输入 mysqld -install 安装mysql服务7.初…

网页无插件视频播放器,支持录像、截图、音视频播放,多路播放等,提供源码下载

前言 本播放器内部采用jessibuca插件接口&#xff0c;支持录像、截图、音视频播放等功能。播放器播放基于ws流&#xff0c;分屏操作支持1分屏、4分屏、6分屏、9分屏方式。 jessibuca工作原理是通过Emscripten将音视频解码库编译成Js&#xff08;WebAssembly&#xff0c;简称was…

Java集合Collection之LinkedList

LinkeList LinkedList&#xff08;双向链表&#xff09;是一种常见的线性数据结构&#xff0c;但是并不会按线性的顺序存储数据。它由一系列节点组成&#xff0c;每个节点包含数据部分和一个指向下一个节点的引用。相比于数组&#xff0c;链表具有动态大小、插入和删除效率高的…

css第一个元素first-child匹配失败原因

<div><p>1</p><h1>2</h1><span>3</span><span>4</span> </div> 1、p:first-child 匹配的是p元素&#xff0c;因为p元素是div的第一个子元素 2、h1:first-child 匹配不到任何元素&#xff0c;因为在这里h1是di…

【电气安全】ASCP电气防火限流式保护器/末端回路线路保护

为什么要使用电气防火限流式保护器&#xff1f; 应急管理部消防救援局统计&#xff0c;在造成电气火灾事故的原因中&#xff0c;最为主要的当为末端线路短路&#xff0c;在电气火灾事故中占比高达70%以上。如何效预防末端线路短路引发的电气火灾事故&#xff1f; 现阶段最为常…

T470 双电池机制

ThinkPad系列电脑牛黑科技双电池管理体系技术,你知道吗&#xff1f; - 北京正方康特联想电脑代理商 上文的地址 在放电情况下&#xff1a;优先让外置电池放电&#xff0c;当放到一定电量后开始让内置电池放电。 在充电情况下&#xff1a;优先给内置电池充电&#xff0c;当充…

Perl: Can‘t locate List/MoreUtils.pm in @INC

BUG: 运行perl 脚本时报错&#xff1a; Cant locate List/MoreUtils.pm in INC (INC contains: /opt/rh/devtoolset-7/root/usr/lib64/perl5/vendor_perl /opt/rh/devtoolset-7/root/usr/share/perl5/vendor_perl /public/home/bgi_wangbinhu/perl5/lib/perl5/5.16.3/x86_64-l…

航空实时监控

1、从Kafka中读取飞机数据&#xff0c;并进行清洗 此步骤在前面的“使用Spark清洗统计业务数据并保存到数据库中”任务阶段应该已经完成。如果没有完成&#xff0c;请参考源代码自行完成。核心类主要有三个&#xff1a;SparkStreamingApplication类、SparkUtil类和MapManager类…

目标检测——YOLOR算法解读

论文&#xff1a;YOLOR-You Only Learn One Representation: Unifified Network for Multiple Tasks 作者&#xff1a;Chien-Yao Wang, I-Hau Yeh, Hong-Yuan Mark Liao 链接&#xff1a;https://arxiv.org/abs/2105.04206 代码&#xff1a;https://github.com/WongKinYiu/yolo…

CAPL如何实现TCP Packet的option字段

在TCP协议中,主机可以根据自身的需要决定TCP通信时是否携带option字段,来扩展TCP功能。option字段属于TCP首部的扩展部分,且是可选项,TCP根据首部中的offset字段值确定TCP报文是否携带option字段。 TCP首部固定的部分有20个字节,如果没有扩展部分(option字段),20个字节…

vue-生成二维码

安装 yarn add qrcodejs2 --save npm install qrcodejs2 --save 使用 <template><div><div id"qrcodeImg"></div><!-- 创建一个div&#xff0c;并设置id --></div> </template> <script> import QRCode from q…

设计模式之简单工厂模式详解

简单工厂模式 工厂模式&#xff1a;工厂方法模式&#xff1b; 低阶&#xff1a;简单工厂模式&#xff1b; 高阶&#xff1a;抽象工厂模式&#xff1b; 1&#xff09;概述 定义一个工厂类&#xff0c;根据参数的不同返回不同类的实例&#xff0c;被创建的实例通常都具有共同…

Linux快速入门,上手开发 01.学习路线

少时曾许凌云志&#xff0c;当取世间第一流 再见少年拉满弓&#xff0c;不惧岁月不飓风 —— 24.3.20 1.Linux的发展历史 2.VM虚拟机的Linux初体验 3.图形化页面设置系统——快速上手 4.命令行操作——向专业前进 5.核心操作命令——必知必会&#xff08;管理企业级权限/定位b…

打印租赁行业现状与未来发展趋势分析

办公设备租赁行业现状与未来发展趋势分析 1. 简介 办公设备租赁行业是近年来快速发展的行业之一&#xff0c;随着数字化办公的普及和企业成本控制的需求增加&#xff0c;办公设备租赁市场呈现出了蓬勃的发展态势。本报告将对办公设备租赁行业的现状、发展历程以及未来发展趋势…

如何从零开始拆解uni-app开发的vue项目(二)

昨天书写了一篇如何从零开始uni-app开发的vue项目,今天准备写一篇处理界面元素动态加载的案例: 背景:有不同类别的设备,每个设备有每日检查项目、每周检查项目、每年检查项目,需要维保人员,根据不同设备和检查类别对检查项目进行处理,提交数据。 首先看一下界面: &l…

C++学习之旅(二)运行四个小项目 (Ubuntu使用Vscode)

如果是c语言学的比较好的同学 可以直接跟着代码敲一遍&#xff0c;代码附有详细语法介绍&#xff0c;不可错过 一&#xff0c;猜数字游戏 #include <iostream> #include <cstdlib> #include <ctime>int main() {srand(static_cast<unsigned int>(tim…

(一)基于IDEA的JAVA基础3

通过之前的内容&#xff0c;我们在建好的文件夹下建一个java文件&#xff0c;我们来在IDEA中写一下之前用记事本写的helloworld&#xff0c;我们先看一下java代码的规范: 1.java程序文件名一定要有意义&#xff0c;首字母一定要大写。 2.class后面的名字:由大小写字母&#x…