C++的 stack和queue 的应用和实现【双端队列的理解和应用】

news2024/11/18 18:18:50

文章目录

  • stack的理解和应用
    • 栈的理解
    • 栈的模拟实现
      • string实现stack
      • vector实现stack
  • queue的理解和应用
    • 队列的理解
      • 队列的模拟实现
  • 双端队列
    • 原理的简单理解
    • deque的缺陷
    • 为什么选择deque作为stack和queue的底层默认容器
    • STL标准库中对于stack和queue的模拟实现
      • stack的模拟实现
      • queue的模拟实现

stack的理解和应用

栈是一种后进先出(Last In, First Out, LIFO)的数据结构,类似于向弹夹里面装子弹和卸子弹。在C++中,栈通常用于实现递归算法、表达式求值、括号匹配等场景。

栈的理解

在C++中,std::stack 是一个容器适配器,它提供了栈的功能。它基于其他容器(如 std::vectorstd::deque 等)实现,但只暴露了栈的接口。栈的基本操作包括:

  • push():向栈顶添加一个元素。
  • pop():移除栈顶的元素。
  • top():访问栈顶元素,但不移除它。
  • empty():检查栈是否为空。
  • size():返回栈中元素的数量。

下面通过一段代码来进行理解

void usestack()
{
	stack<int> st;//栈的创建

	st.push(1);//入栈
	st.push(2);
	st.push(3);
	st.push(4);
	st.push(5);

	cout << st.size() << endl;//返回栈中的元素的数量

	while (!st.empty())//判断栈是否为空
	{
		cout << st.top() << " ";//输出栈顶元素
		st.pop();//出栈
	}
	cout << endl;
}

栈的模拟实现

栈的实现通常使用一个动态数组或者链表来存储数据,但栈的接口只允许从栈顶进行操作。
所以通常我们进行实现stack直接用vector或者string来实现就非常的方便

string实现stack

#include <iostream>
#include <string>
#include <stdexcept>

class StringStack {
private:
    std::string data;

public:
    void push(const std::string& item) {
        data += item + " ";
    }

    void pop() {
        if (empty()) {
            throw std::out_of_range("Stack<>::pop(): empty stack");
        }
        size_t lastSpace = data.rfind(' ');
        if (lastSpace != std::string::npos) {
            data.erase(lastSpace);
        } else {
            data.clear();
        }
    }

    std::string top() const {
        if (empty()) {
            throw std::out_of_range("Stack<>::top(): empty stack");
        }
        size_t lastSpace = data.rfind(' ');
        return data.substr(lastSpace + 1);
    }

    bool empty() const {
        return data.empty();
    }

    size_t size() const {
        return data.size();
    }
};

vector实现stack

#include <iostream>
#include <vector>
#include <stdexcept>

template <typename T>
class VectorStack {
private:
    std::vector<T> data;

public:
    void push(const T& item) {
        data.push_back(item);
    }

    void pop() {
        if (empty()) {
            throw std::out_of_range("Stack<>::pop(): empty stack");
        }
        data.pop_back();
    }

    T top() const {
        if (empty()) {
            throw std::out_of_range("Stack<>::top(): empty stack");
        }
        return data.back();
    }

    bool empty() const {
        return data.empty();
    }

    size_t size() const {
        return data.size();
    }
};

这样就可以很好的模拟实现stack

queue的理解和应用

队列是一种先进先出(First In, First Out, FIFO)的数据结构,它类似于排队等候服务的队伍:新来的人排在队伍的末尾,而服务总是从队伍的最前面开始。在C++中,队列通常用于任务调度、消息队列、打印任务管理等场景。

队列的理解

在C++中,std::queue 是一个容器适配器,它提供了队列的功能。它基于其他容器(如 std::dequestd::list 等)实现,但只暴露了队列的接口。队列的基本操作包括:

  • enqueue():向队列尾部添加一个元素。
  • dequeue():移除队列头部的元素。
  • front():访问队列头部元素,但不移除它。
  • empty():检查队列是否为空。
  • size():返回队列中元素的数量。

下面通过一段代码来进行理解

void usequeue()
{
	queue<int> qu;

	qu.push(1);
	qu.push(2);
	qu.push(3);
	qu.push(4);
	qu.push(5);

	cout << qu.size() << endl;
	cout << qu.back() << endl;//输出队列最后一个元素

	while (!qu.empty())//判断队列是否为空
	{
		cout << qu.front() << " ";//输出队列头元素
		qu.pop();//出队列
	}
	cout << endl;
}

队列的模拟实现

队列的实现通常使用一个动态数组或者链表来存储数据,但队列的接口只允许从队列尾部进行添加操作,从队列头部进行移除操作。
因为string在头插和头删上实际拿上大大的增加,所以一般实现queue就用strack进行

#include <iostream>
#include <list>
#include <stdexcept>

template <typename T>
class ListQueue {
private:
    std::list<T> data;

public:
     void enqueue(const T& item) {
         data.push_back(item);
     }

     void dequeue() {
         if (data.empty()) {
             throw std::out_of_range("Queue<>::dequeue(): empty queue");
         }
         data.pop_front();
     }

     T front() const {
         if (data.empty()) {
             throw std::out_of_range("Queue<>::front(): empty queue");
         }
         return data.front();
     }

     bool empty() const {
         return data.empty();
     }

     size_t size() const {
         return data.size();
     }
};

双端队列

在实现栈和队列的时候liststing或多或少都会有一些问题或者小毛病那有没有一个老大哥呢?这时候就引出了一个新的容器适配器
deque(双端队列):是一种双开口的"连续"空间的数据结构,双开口的含义是:可以在头尾两端进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与list比较,空间利用率比较高。
在这里插入图片描述

原理的简单理解

那么这个老大哥是怎么进行实现的呢?

deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组,其底层结构如下图所示:
在这里插入图片描述

双端队列底层是一段假象的连续空间,实际是分段连续的,为了维护其“整体连续”以及随机访问的假象,落在了deque的迭代器身上,因此deque的迭代器设计就比较复杂,如下图所示:
在这里插入图片描述
那deque是如何借助其迭代器维护其假想连续的结构呢?
通过一个中控器(图中map)来进行多个buffer的连接进而实现”连续空间“然后通过两个迭代器startfinish进行查找和遍历每个迭代器都有四个节点curfirst,last分别是在buffer指向任意位置,头尾的。而node是指向中控器进而连接下一个bufferfinsh指向随后一个buffer的最后一个元素二点下一个位置进行结尾的定位
在这里插入图片描述

deque的缺陷

  1. vector比较,deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不需要搬移大量的元素,因此其效率是必vector高的。
  2. list比较,其底层是连续空间,空间利用率比较高,不需要存储额外字段。
  3. 但是,deque有一个致命缺陷:不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下,而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构时,大多数情况下优先考虑vectorlistdeque的应用并不多,而目前能看到的一个应用就是,STL用其作为stackqueue的底层数据结构。

所以这么看这个老大哥也不是万能的

为什么选择deque作为stack和queue的底层默认容器

stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()pop_back()操作的线性结构,都可以作为stack的底层容器,比如vectorlist都可以;queue是先进先出的特殊线性数据结构,只要具有push_backpop_front操作的线性结构,都可以作为queue的底层容器,比如list。但是STL中对stackqueue默认选择deque作为其底层容器,主要是因为:

  1. stackqueue不需要遍历(因此stackqueue没有迭代器),只需要在固定的一端或者两端进行操作。
  2. 在stack中元素增长时,deque比vector的效率高(扩容时不需要搬移大量数据);queue中的元素增长时,deque不仅效率高,而且内存使用率高。
    结合了deque的优点,而完美的避开了其缺陷。

STL标准库中对于stack和queue的模拟实现

stack的模拟实现

#include<deque>
namespace bite
{
 template<class T, class Con = deque<T>>
 //template<class T, class Con = vector<T>>
 //template<class T, class Con = list<T>>
 class stack
 {
 public:
 stack() {}
 void push(const T& x) 
 {
 _c.push_back(x);
 }
 
 void pop() 
 {
 _c.pop_back();
 }
 
 T& top() 
 {
 return _c.back();
 }
 
 const T& top()const 
 {
 return _c.back();
 }
 
 size_t size()const 
 {
 return _c.size();
 }
 
 bool empty()const 
 {
 return _c.empty();
 }
 
 private:
 Con _c;
 };
}

queue的模拟实现

#include<deque>
#include <list>
namespace bite
{
 template<class T, class Con = deque<T>>
 //template<class T, class Con = list<T>>
 class queue
 {
 public:
 queue() 
 {}
 
 void push(const T& x) 
 {
 _c.push_back(x);
 }
 
 void pop() 
 {
 _c.pop_front();
 }
 
 T& back() 
 {
 return _c.back();
 }
 
 const T& back()const 
 {
 return _c.back();
 }
 
 T& front() 
 {
 return _c.front();
 }
 
 const T& front()const 
 {
 return _c.front();
 }
 
 size_t size()const 
 {
 return _c.size();
 }
 
 bool empty()const 
 {
 return _c.empty();
 }
 
 private:
 Con _c;
 };
}

感觉好的话点个赞
请添加图片描述

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

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

相关文章

智能停车场物联网远程监控解决方案

智能停车场物联网远程监控解决方案 智能停车场物联网远程监控解决方案是一种集成了现代物联网技术、大数据分析以及云计算等先进技术手段&#xff0c;对停车场进行全面智能化管理的综合系统。它通过实时感知、精准采集和高效传输各类停车数据&#xff0c;实现对停车场运营状态…

基于Springboot的学校防疫物资管理平台(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的学校防疫物资管理平台&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系…

Data-efficient Fine-tuning for LLM-based Recommendation

目录 Introduction 利用大型语言模型&#xff08;LLM&#xff09;进行推荐最近引起了相当大的关注&#xff0c;其中微调在 LLM 的适应中发挥着关键作用。然而&#xff0c;在快速扩展的推荐数据上微调LLMs的成本限制了其实际应用。为了应对这一挑战&#xff0c;小样本微调提供了…

RabbitMQ3.13.x之十_流过滤的内部结构设计与实现

RabbitMQ3.13.x之十_流过滤的内部结构设计与实现 文章目录 RabbitMQ3.13.x之十_流过滤的内部结构设计与实现1. 概念1. 消息发布2. 消息消费 2. 流的结构1. 在代理端进行过滤2. 客户端筛选3. JavaAPI示例4. 流过滤配置5. AMQP上的流过滤6. 总结 3. 相关链接 1. 概念 流过滤的思…

linux练习-交互式传参

在shell脚本中&#xff0c;read 向用户显示一行文本并接受用户输入 #!/bin/bash read -p 依次输入你的姓名、年龄、家乡 name age home echo 我是$name,年龄$age,我来自$home

C++数据结构与算法——二叉树公共祖先问题

C第二阶段——数据结构和算法&#xff0c;之前学过一点点数据结构&#xff0c;当时是基于Python来学习的&#xff0c;现在基于C查漏补缺&#xff0c;尤其是树的部分。这一部分计划一个月&#xff0c;主要利用代码随想录来学习&#xff0c;刷题使用力扣网站&#xff0c;不定时更…

优衣库门店可视化与顾客拜访数据分组-Python数据分析项目

文章目录 项目背景1 引言2 数据说明 一、数据导入及预处理1 数据导入2 数据观察2.1 查看数据形状2.2 检查缺失值2.3 有无重复值 3 数据预处理3.1 获取详细地址3.2 批量获取经纬度3.2.1 安装geopy包3.2.2 批量获取经纬度 二、优衣库门店可视化1 数据获取1.1 读取地点数据1.2 筛选…

c语言数据结构(10)——冒泡排序、快速排序

欢迎来到博主的专栏——C语言数据结构 博主ID&#xff1a;代码小豪 文章目录 冒泡排序冒泡排序的代码及原理快速排序快速排序的代码和原理快速排序的其他排序方法非递归的快速排序 冒泡排序 相信冒泡排序是绝大多数计科学子接触的第一个排序算法。作为最简单、最容易理解的排序…

创新性的智慧公厕技术研发与应用

智慧公厕&#xff0c;作为城市基础设施的重要组成部分&#xff0c;扮演着提供舒适便捷卫生服务的角色。智慧公厕源头实力厂家广州中期科技有限公司&#xff0c;通过技术创新与应用升级&#xff0c;打造标杆性的智慧公厕整体解决方案。通过创新性的技术应用&#xff0c;智慧公厕…

字节新作:图像生成质量超越DiT

&#x1f31f;每日更新最新高质量论文&#xff0c;关注我&#xff0c;时刻关注最新大模型进展。&#x1f31f; &#x1f4cc; 元数据概览&#xff1a; 标题&#xff1a;Visual Autoregressive Modeling: Scalable Image Generation via Next-Scale Prediction作者&#xff1a…

Jupyter IPython帮助文档及其魔法命令

1.IPython 的帮助文档 使用 help() 使用 ? 使用 &#xff1f;&#xff1f; tab 自动补全 shift tab 查看参数和函数说明 2.运行外部 Python 文件 使用下面命令运行外部 Python 文件&#xff08;默认是当前目录&#xff0c;也可以使用绝对路径&#xff09; %run *.py …

数据湖概述:大数据演进阶段-数据湖

文章目录 一. 大数据发展过程1. 离线大数据平台2. Lambda架构&#xff1a;速度层批层3. Kappa架构&#xff1a;流批一体4. 大数据架构痛点总结 二. 数据湖助力于解决数据仓库痛点问题1. 数据湖特点2. 开源数据湖的架构 三. 数据湖和数据仓库理念的对比1. 数据湖和数据仓库对比2…

LeetCode 热题 100 | 贪心算法

目录 1 121. 买卖股票的最佳时机 2 55. 跳跃游戏 3 45. 跳跃游戏 II 4 763. 划分字母区间 菜鸟做题&#xff0c;语言是 C 1 121. 买卖股票的最佳时机 解题思路&#xff1a; 维护一个变量 max_pricemax_price 用于存储排在 i 天之后的股票最高价格第 i 天的最高利润 …

Day5-Hive的结构和优化、数据文件存储格式

Hive 窗口函数 案例 需求&#xff1a;连续三天登陆的用户数据 步骤&#xff1a; -- 建表 create table logins (username string,log_date string ) row format delimited fields terminated by ; -- 加载数据 load data local inpath /opt/hive_data/login into table log…

armlinux-外部中断

s3c2440的中断框图 如果我们单纯配置一个按键的外部中断&#xff0c;就不存在子中断与优先级的问题。 由于是按键的外部中断&#xff0c;通过引脚的高低电平来触发。所以我们要先配置引脚的功能。 我们使用按键1&#xff0c;终端源为EINT8&#xff0c;对应引脚GPG0 通过用户手…

Stable diffusion 加载扩展列表报错解决方法

项目场景&#xff1a; 在使用Stable diffusion webui时&#xff0c;使用扩展列表出现错误 问题描述 点击loadfrom后&#xff0c;出现加载扩展列表报错 原因分析&#xff1a; 下载的扩展的时候&#xff0c;都是github 的url&#xff0c;需要科学上网&#xff0c;如果不能科学…

深澜计费管理系统 任意文件读取漏洞复现

0x01 产品简介 深澜计费管理系统是是一套完善的领先的具有复杂生物型特征的弹性认证计费系统。系统主要由 AAA 认证计费平台、系统运营维护管理平台、用户及策略管理平台、用户自助服务平台、智能客户端模块、消息推送模块、数据统计模块组成。目前在全球为超过 2500 家客户提…

MicroPython 树莓派 RP2 入门教程

系列文章目录 前言 Raspberry Pi Pico 开发板&#xff08;图片来源&#xff1a;Raspberry Pi 基金会&#xff09;。 以下是 Raspberry Pi RP2xxx 板的快速参考资料。如果您是第一次使用该开发板&#xff0c;了解微控制器的概况可能会对您有所帮助&#xff1a; 一、关于 RP2xxx…

【项目实战】——商品管理的制作完整代码

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

数据结构:详解【树和二叉树】

1. 树的概念及结构&#xff08;了解&#xff09; 1.1 树的概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝…