【042】解密C++ STL:深入理解并使用queue容器

news2024/11/27 13:46:55

解密C++ STL:深入理解并使用queue容器

  • 引言
  • 一、queue容器概述
  • 二、queue容器的底层实现原理
  • 三、queue容器常用API
  • 四、queue的使用案例
    • 4.1、使用queue容器实现一个高效的算法
    • 4.2、实现生产者-消费者模型
  • 总结

引言


💡 作者简介:一个热爱分享高性能服务器后台开发知识的博主,目标是通过理论与代码实践的结合,让世界上看似难以掌握的技术变得易于理解与掌握。技能涵盖了多个领域,包括C/C++、Linux、Nginx、MySQL、Redis、fastdfs、kafka、Docker、TCP/IP、协程、DPDK等。
👉
🎖️ CSDN实力新星、CSDN博客专家
👉
🔔 专栏介绍:从零到c++精通的学习之路。内容包括C++基础编程、中级编程、高级编程;掌握各个知识点。
👉
🔔 专栏地址:C++从零开始到精通
👉
🔔 博客主页:https://blog.csdn.net/Long_xu


🔔 上一篇:【041】从零开始:逐步学习使用C++ STL中的stack容器

一、queue容器概述

queue是一种常见的容器数据结构,它可以用来存储一组元素,遵循先进先出(First-In-First-Out,FIFO)的原则。简单来说,最先进入队列的元素将会最先被移除,而最后进入队列的元素将会最后被移除。

queue容器的主要特点包括:

  • 先进先出原则:队列是按照元素的进入顺序进行处理的。新元素被添加到队列的尾部,而从队列中移除元素是从队列的头部进行的。

  • 有限大小:通常情况下,queue容器的大小是有限的。当队列已满时,试图向队列中添加新元素将会失败,或者在特定的实现中可能会覆盖旧元素。

常见操作:queue容器通常支持以下基本操作:

  1. 入队(enqueue):向队列尾部添加一个新元素。
  2. 出队(dequeue):从队列头部移除一个元素。
  3. 队头元素(front):获取队列头部的元素,但不会移除它。
  4. 队尾元素(back):获取队列尾部的元素,但不会移除它。

底层实现:queue容器的底层实现可以使用不同的数据结构,例如链表或数组。具体的实现可能会影响操作的时间复杂度。

queue容器在很多情况下都很有用,特别是在需要按照先后顺序处理数据的场景,比如任务调度、广度优先搜索等。

queue容器有两个口,允许从一端新增元素,从另一端移除元素。

在这里插入图片描述
queue所有元素的进出都必须符合"先进先出"的条件,只有queue的顶端元素,才有机会被外界取用。queue不提供遍历功能,也不提供迭代器。

二、queue容器的底层实现原理

queue容器是一种先进先出(FIFO)的数据结构,其底层实现原理基于队列模型。队列模型类似于现实生活中排队的情景,最先进入队列的元素将首先被处理,而最后进入队列的元素将在队尾等待。

常见的queue实现方式包括数组、链表和环形缓冲区,每种实现方式都有其优劣势:

数组实现:

  • 原理:使用数组作为底层数据结构,通过指针或索引来跟踪队头和队尾元素。
  • 优势:随机访问的时间复杂度为O(1),因此可以快速访问队头和队尾元素。内存连续分配,利于CPU缓存命中,性能相对较好。
  • 劣势:插入和删除元素时,可能需要移动其他元素以保持连续性,导致时间复杂度为O(n)。当队列元素数量较大时,频繁的内存搬移可能会影响性能。

链表实现:

  • 原理:使用链表作为底层数据结构,通过指针来连接节点,并使用指针跟踪队头和队尾元素。
  • 优势:插入和删除元素时,只需调整指针指向,时间复杂度为O(1)。不会有数组的内存搬移问题。
  • 劣势:随机访问的时间复杂度为O(n),需要从头节点开始遍历到指定位置,效率较低。每个节点都需要额外的指针存储空间,可能会占用更多内存。

环形缓冲区实现:

  • 原理:使用固定大小的循环数组作为底层数据结构,利用取模运算实现环形特性。
  • 优势:没有链表的指针开销,可以更高效地使用内存。插入和删除元素的时间复杂度为O(1)。
  • 劣势:由于固定大小,当队列满时,需要进行额外的处理,如扩展队列大小。

三、queue容器常用API

使用<queue>头文件,C++中的队列容器通常使用std::queue模板类来表示。

(1)push():向队列尾部添加一个元素。

#include <iostream>
#include <queue>

int main() {
    std::queue<int> myQueue;
    myQueue.push(10);
    myQueue.push(20);
    return 0;
}

(2)pop():从队列头部移除一个元素。

#include <iostream>
#include <queue>

int main() {
    std::queue<int> myQueue;
    myQueue.push(10);
    myQueue.push(20);
    myQueue.pop(); // Removes the element 10 from the front
    return 0;
}

(3)front():访问队列头部元素的值(但不删除它)。

#include <iostream>
#include <queue>

int main() {
    std::queue<int> myQueue;
    myQueue.push(10);
    myQueue.push(20);
    int frontElement = myQueue.front(); // frontElement will be 10
    return 0;
}

(4)back():访问队列尾部元素的值(但不删除它)。

#include <iostream>
#include <queue>

int main() {
    std::queue<int> myQueue;
    myQueue.push(10);
    myQueue.push(20);
    int backElement = myQueue.back(); // backElement will be 20
    return 0;
}

(5)size():获取队列中元素的数量。

#include <iostream>
#include <queue>

int main() {
    std::queue<int> myQueue;
    myQueue.push(10);
    myQueue.push(20);
    size_t queueSize = myQueue.size(); // queueSize will be 2
    return 0;
}

(6)empty():检查队列是否为空。

#include <iostream>
#include <queue>

int main() {
    std::queue<int> myQueue;
    if (myQueue.empty()) {
        std::cout << "Queue is empty." << std::endl;
    } else {
        std::cout << "Queue is not empty." << std::endl;
    }
    return 0;
}

四、queue的使用案例

4.1、使用queue容器实现一个高效的算法

使用queue容器可以实现许多高效的算法,例如广度优先搜索(BFS)算法和任务调度等。

#include <iostream>
#include <queue>
#include <vector>

using namespace std;

void bfs(vector<vector<int>>& graph, int start) {
    int n = graph.size();
    vector<bool> visited(n, false);
    
    queue<int> q;
    q.push(start);
    visited[start] = true;
    
    while (!q.empty()) {
        int node = q.front();
        q.pop();
        
        cout << "Visited: " << node << endl;
        
        for (int neighbor : graph[node]) {
            if (!visited[neighbor]) {
                q.push(neighbor);
                visited[neighbor] = true;
            }
        }
    }
}

int main() {
    // 以邻接表形式表示图
    vector<vector<int>> graph = {
        {1, 2},
        {0, 2, 3},
        {0, 1, 3},
        {1, 2}
    };
    
    bfs(graph, 0);  // 从节点0开始进行BFS
    
    return 0;
}

使用queue容器实现了基于邻接表的BFS算法。该算法通过队列的先进先出特性,在遍历节点时按照广度优先的顺序遍历其邻居节点。

4.2、实现生产者-消费者模型

生产者-消费者模型是一种常见的并发编程模式,其中有一个或多个生产者将数据放入共享的缓冲区(队列),而一个或多个消费者从队列中获取数据并进行处理。这种模型可以很好地解决生产者和消费者之间的数据传递和协调问题。

在这个模型中,queue容器可以被用来实现共享的缓冲区。生产者将数据放入队尾,而消费者从队首获取数据。queue的FIFO特性非常适合这种场景,确保了数据按照插入顺序进行处理。

使用queue容器来实现生产者-消费者模型:

#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>

const int MAX_QUEUE_SIZE = 5; // 缓冲区的最大容量

std::queue<int> dataQueue;
std::mutex mtx;
std::condition_variable cv;

void producer() {
    for (int i = 1; i <= 10; ++i) {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, [] { return dataQueue.size() < MAX_QUEUE_SIZE; }); // 等待缓冲区不满

        dataQueue.push(i);
        std::cout << "Producer produced: " << i << std::endl;

        lock.unlock();
        cv.notify_all(); // 通知消费者可以消费数据了
    }
}

void consumer() {
    for (int i = 1; i <= 10; ++i) {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, [] { return !dataQueue.empty(); }); // 等待缓冲区不空

        int data = dataQueue.front();
        dataQueue.pop();
        std::cout << "Consumer consumed: " << data << std::endl;

        lock.unlock();
        cv.notify_all(); // 通知生产者可以继续生产数据
    }
}

int main() {
    std::thread producerThread(producer);
    std::thread consumerThread(consumer);

    producerThread.join();
    consumerThread.join();

    return 0;
}

有一个最大容量为5的缓冲区,生产者和消费者都会执行10次生产和消费操作。当缓冲区满时,生产者会等待,直到消费者取走数据,从而腾出空间。同样地,当缓冲区为空时,消费者会等待,直到生产者放入新的数据。

通过queue容器和互斥锁(std::mutex)以及条件变量(std::condition_variable)的结合使用,我实现了一个简单的线程安全的生产者-消费者模型。这样可以确保生产者和消费者之间的操作不会发生竞态条件(race condition),从而实现了数据的安全传递和处理。

总结

头文件包含: 要使用queue容器,需要包含<queue>头文件。

声明: 在C++中声明一个queue容器可以使用如下语法:

std::queue<数据类型> queue_name;

这里的数据类型可以是任何C++支持的数据类型,例如int、double、char等。

插入元素: 使用push()成员函数将元素添加到队列的末尾。例如:

std::queue<int> myQueue;
myQueue.push(10);
myQueue.push(20);
myQueue.push(30);

此时,myQueue中的元素为10, 20, 30。

访问队首元素: 使用front()成员函数可以访问队列的第一个元素(队首元素),但不会将其从队列中移除。例如:

int firstElement = myQueue.front();

在上面的示例中,firstElement将获得值10。

移除队首元素: 使用pop()成员函数可以将队列的第一个元素(队首元素)移除。例如:

myQueue.pop();

现在,myQueue中的元素为20, 30。

判断队列是否为空: 使用empty()成员函数可以检查队列是否为空,如果为空则返回true,否则返回false。例如:

if (myQueue.empty()) {
    // 队列为空
} else {
    // 队列不为空
}

获取队列中元素的个数: 使用size()成员函数可以获取队列中元素的个数。例如:

int queueSize = myQueue.size();

在上面的示例中,queueSize将获得值2。

特性:

queue容器是一个FIFO(First-In-First-Out)数据结构,即先进先出。队首元素是最先被插入的元素,队尾元素是最后被插入的元素。
queue不允许直接访问中间或随机位置的元素。只能访问队首和队尾元素,并且只能从队首删除元素。

在这里插入图片描述

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

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

相关文章

基于linux下的高并发服务器开发(第二章)- Linux多进程开发

基于linux下的高并发服务器开发&#xff08;第二章&#xff09;- 2.1 进程概述_呵呵哒(&#xffe3;▽&#xffe3;)"的博客-CSDN博客01 / 程序和进程02 / 单道、多道程序设计03 / 时间片04/ 并行和并发05/ 进程控制块&#xff08;PCB&#xff09;https://blog.csdn.net/w…

聊天机器人如何增加电子商务销售额

聊天机器人和自动化对企业和客户来说都是福音。自动对话和聊天机器人&#xff08;以下统称为“自动化”&#xff09;通过自动回答问题或分配会话信息来帮助用户浏览品牌网站或电商商店。即时答案对客户来说非常有用&#xff0c;使用自动化也可以让原本与客户聊天的客服员工专注…

jQueryAPI

文章目录 1.jQuery 选择器1.1 jQuery 基础选择器1.2 jQuery 层级选择器1.3 隐式迭代1.4 jQuery 筛选选择器1.5 jQuery 筛选方法1.6 jQuery 里面的排他思想1.7 链式编程 2.jQuery 样式操作2.1 操作 css 方法2.2 设置类样式方法2.3 类操作与className区别 3.jQuery 效果3.1 显示隐…

Nginx下载和安装教程、Nginx目录结构、Nginx具体应用

1、Nginx概述 Nginx是一款轻量级的开源Web服务器软件&#xff0c;也是一种反向代理服务器。它以其高性能和灵活性而被广泛应用于互联网领域。本文将介绍Nginx的概述、下载和安装以及目录结构。 &#xff08;1&#xff09;Nginx介绍 Nginx最初由Igor Sysoev开发&#xff0c;目…

【MangoDB】学习笔记

这里写自定义目录标题 1 简介1.1 和MySQL的概念对比1.2 数据类型 1 简介 1.1 和MySQL的概念对比 1.2 数据类型

005.PADS VX2.4自定义快捷键设置及修改

005.PADS VX2.4自定义快捷键设置及修改 若部分快捷键不符合操作习惯或者部分功能系统未设置快捷键&#xff0c;可自定义快捷键功能&#xff0c;自定义快捷键功能需要注意设置的按键需要与无模命令区分&#xff0c;以免与无模命令冲突&#xff1b;如若需要修改某个快捷功能&…

隐马尔可夫HMM算法

算法简介&#xff1a; 一个系统每时每刻都存在着不同的状态&#xff0c;他们的状态由各种复杂的机制形成&#xff0c;且状态随着变化而不断改变&#xff0c;而这些复杂的机制就是一些高度非线性且复杂的射关系。 举一个例子。例如&#xff1a;一辆汽车在一条从左往右的道路上行…

华为OD机试真题 Java 实现【数据分类】【2023 B卷 100分】,附详细解题思路

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路1、输入2、输出3、解题思路 五、Java算法源码六、效果展示1、输入2、输出 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&…

去掉mysql数据库表中日期时间字段的6位微秒

问题&#xff1a; 在DRF框架的models.py中有一个字段&#xff1a; create_time models.DateTimeField(auto_now_addTrue) 问题是在向数据库中插入数据时&#xff0c;此字段会带有6位的微秒&#xff0c;对精度要求没那么高&#xff08;看着很不爽&#xff09;&#xff0c;需…

每日一题——多数元素

多数元素 题目链接 方法一&#xff1a;暴力解法 直接利用两层循环&#xff0c;外层循环用来枚举数组的每一个元素&#xff0c;内层循环用来计算每个元素出现的次数&#xff0c;这样就可以求出多数元素了。 显然&#xff0c;这个方法的时间复杂度为O(N^2)&#xff0c;效率太低…

C#is、as关键字及获取当前活动窗体的实例

这篇日志记录一下C#中is关键字及as关键字的用法。 Is&#xff1a;判断检查对象是否与给定类型兼容 As&#xff1a;将对象转换为指定类型&#xff08;强转&#xff09;&#xff0c;就跟&#xff08;int&#xff09;这样的用法是一样的。 获取当前窗体的活动子窗体。 有一个属…

【暑期每日一练】 day6

目录 选择题 &#xff08;1&#xff09; 解析 &#xff08;2&#xff09; 解析 &#xff08;3&#xff09; 解析 &#xff08;4&#xff09; 解析 &#xff08;5&#xff09; 解析 编程题 题一 描述 示例 提示 解析 代码实现 题二 描述 示例 提示 解…

如何将表格中的状态数据转换为Tag标签显示

考虑到系统前端页面的美观程度&#xff0c;通常通过Tag标签来代替某条数据中的状态信息。仅通过一点操作&#xff0c;便能够使得页面美观程度得到较大提升&#xff0c;前后对比如下所示。代码基于Vue以及Element-ui组件实现。 修改前&#xff1a; 修改后&#xff1a; 修改前…

阿里内部一份手打 524 页《Java 中高级核心知识》令人醍醐灌顶

说在前面 知乎上有个很热门的话题&#xff1a;中国的程序员数量是否已经饱和或者过剩&#xff1f; 今年大家都有一个共同的感受&#xff1a;工作不好找&#xff0c;面试越来越难。 其实&#xff0c;造成这种现象不仅是因为今年受疫情影响&#xff0c;倒闭了很多公司&#xff…

Excalidraw 简介及 Docker Compose 部署指南

家人们好&#xff0c;我们在工作生活中经常需要画些图&#xff0c;我们往期了已经出过draw-io私有化部署的文章了&#xff0c;今天我要向大家介绍一款名为 Excalidraw 的绘图工具&#xff0c;这款工具了我个人非常喜欢使用&#xff0c;是因为它可以修改成类似于手写体的字体&am…

IDEA Groovy 脚本一键生成实体类<mybatisplus>

配置数据库&#xff08;mysql&#xff09; 一键生成&#xff08;右键点击table&#xff09; 配置自己的groovy脚本 import com.intellij.database.model.DasTable import com.intellij.database.util.Case import com.intellij.database.util.DasUtil import com.intellij.data…

Vue过度与动画

Test.vue:元素外面包一层transition&#xff0c;展示的时候就默认调用style里面的v-enter-action和v-leave-action执行进入和退出效果&#xff0c;appear上来默认展示动画效果 <template><div><button click"isShow !isShow">显示/隐藏</butto…

消息队列- 背景知识

这里写目录标题 前言消息队列消息队列的作用常见的消息队列消息队列的核心概念BrokerServer核心概念消息队列的核心API消息队列与消费者之间的工作模式交换机的类型消息队列的持久化 总结 前言 消息队列,不知道大家是否陌生,如果说消息队列感到陌生的话, 有一个模型肯定大家都…

【驱动开发day4作业】

头文件代码 #ifndef __HEAD_H__ #define __HEAD_H__ typedef struct{unsigned int MODER;unsigned int OTYPER;unsigned int OSPEEDR;unsigned int PUPDR;unsigned int IDR;unsigned int ODR; }gpio_t; #define PHY_LED1_ADDR 0X50006000 #define PHY_LED2_ADDR 0X50007000 #…

springboot 自定义注解 ,实现接口限流(计数器限流)【强行喂饭版】

思路&#xff1a;通过AOP拦截注解标记的方法&#xff0c;在Redis中维护一个计数器来记录接口访问的频率&#xff0c; 并根据限流策略来判断是否允许继续处理请求。 另一篇&#xff1a;springboot 自定义注解 &#xff0c;aop切面Around&#xff1b; 为接口实现日志插入【强行喂…