【C++】STL栈和队列基本功能介绍、题目练习和模拟实现(容器适配器)

news2024/11/24 16:36:31

stack && queue 基本功能介绍、练习和模拟实现

    • 前言
    • 正式开始
      • 基本函数功能
      • 三道经典栈题目讲解
        • 最小栈
        • 栈的弹出压入顺序
        • 逆波兰表达式求值
      • 模拟实现
        • stack
        • queue
      • deque

在这里插入图片描述

前言

本篇基本功能不会介绍太多,主要是说一下STL库中的接口,还是在这个网站上的:cplusplus

基本功能介绍完后会有几道题来帮助消化。

然后就是模拟实现了。

前面讲string、vector、list的时候都是将函数接口讲解和模拟实现分开来说的,但是栈和队列接口太少了,没必要分开说,再说我相信点进来的多少都是了解栈和队列的特性的。

或不多说,开始。

正式开始

先给两张图:
在这里插入图片描述
在这里插入图片描述
每句话前面圈红色的部分是适配器的意思。
啥时适配器呢?
现在怕是不好讲,等会讲模拟实现的时候你们就明白了。

这里再提一嘴,适配器是STL的六大组件(容器、适配器、迭代器、算法、函数对象、分配器)之一。
关于六大组件,这里给上一篇博客:C++ STL六大组件简介。如果看不懂没关系,多学就好。

基本函数功能

stack
在这里插入图片描述

queue
在这里插入图片描述

就这么几个函数接口,加起来还没一个vector的多。
有的同学可能要问为啥没迭代器啥的,因为栈和队列的特性是先进后出和后进先出,不需要我们去遍历,如果我们能够遍历栈和队列,就会出问题,其特性就会被改变。

那么就直接给例子了。

栈的:
在这里插入图片描述
这个例子已经把所有常用的接口都给了,就不细讲了,栈和队列重要的地方不是讲接口怎么用,重要的是有时做题要用到二者,重要的是解决问题的思路。

再给queue的例子:
在这里插入图片描述
也是不说那么多,front和back就是队头和队尾数据。

下面直接给题。

三道经典栈题目讲解

最小栈

链接:最小栈

题目如下:
在这里插入图片描述

题目解读:
本题是让我们实现一个栈,栈中存放的数据元素类型为int,这个栈要能够在O(1)时间复杂度下找出栈中的最小元素。

解题思路:
我们可以用两个栈来实现,一个普通栈来正常存放我们的数据,一个最小元素栈用来存放最小元素。
最小元素栈,当普通栈中push进一个小于等于历史最小元素的数据(假如为x)时候就要更新最小元素栈,将 x push到最小元素栈中。

代码实现:

class MinStack {
public:
	
	// 这里的构造函数写不写无所谓,因为类中只有两个stack自定义类型,初始化时
	//会自动调用stack的构造函数。这里就算构造函数是空的,也会在初始化列表处
	//调用。把构造函数删除也是,会自动调用stack的构造函数。
    MinStack() {

    }
    
    // push时若最小元素栈为空,就直接push进去,不为空,先比较,val小于等于栈顶就入栈
    void push(int val) {
        _normal_st.push(val);
        if(_min_st.empty())
            _min_st.push(val);
        else if(val <= _min_st.top())
            _min_st.push(val);
    }
    
    void pop() {
    	// 这里是否判断为空是无所谓的,因为逻辑上是不可能为空的。
        if(!_min_st.empty() && _normal_st.top() == _min_st.top())
            _min_st.pop();

        _normal_st.pop();
    }
    
    int top() {
        return _normal_st.top();
    }
    
    int getMin() {
        return _min_st.top();
    }
private:
    stack<int> _normal_st;
    stack<int> _min_st;
};

在这里插入图片描述

栈的弹出压入顺序

链接:栈的弹出压入顺序

题目:
在这里插入图片描述

题目解析:本题就是想要判断一下所给的入栈顺序是否能够实现对应的出栈顺序。

解题思路:模拟实现入栈过程。
每次pushV都直接入栈,入完栈后看栈顶元素与popV中的元素是否相等。
若不相等,则继续入栈;若相等,就pop栈,并继续比对popV的下一个元素与栈顶元素是否相等。

当push最后一个元素并比对完毕后,若栈不为空或popV中还有元素未比对,就返回false,若栈为空或popV中元素比对完毕,就返回true。

图解(能力有限,不会做动图,各位将就看看):

成立的:
[1,2,3,4,5],[4,5,3,2,1]

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

不成立的:
[1,2,3,4,5],[4,3,5,1,2]

在这里插入图片描述
在这里插入图片描述

bool IsPopOrder(vector<int>& pushV, vector<int>& popV) 
{
   int popI = 0;
   stack<int> st;
   for(auto puv : pushV)
   {
       st.push(puv);

       while(!st.empty() && st.top() == popV[popI])
       {
           st.pop();
           ++popI;
       }
   }

   return st.empty();
   // return popI == popV.size();
	//二者都可判断是否成立。
}

逆波兰表达式求值

链接:逆波兰表达式求值

题目:
在这里插入图片描述

题目解析:其实就是用后缀表达式求值。
我们平时1 + 1这样的表达式是中缀表达式,写成后缀就是1 1 +。

解题思路:定义一个栈,依次遍历字符串数组,遇到数字就入栈,遇到算数运算符就将栈顶两个元素做对应的运算,得到的结果继续入栈。

在这里插入图片描述
在这里插入图片描述
代码:

class Solution 
{
public:
    int evalRPN(vector<string>& tokens) 
    {
        stack<int> st;
        for(auto str : tokens)
        {
            if(str == "+" || str == "-"
            || str == "*" || str == "/")
            {
                int right = st.top();
                st.pop();
                int left = st.top();
                st.pop();
                switch(str[0])
                {
                    case '+':
                        st.push(left + right);
                        break;
                    case '-':
                        st.push(left - right);
                        break;
                    case '*':
                        st.push(left * right);
                        break;
                    case '/':
                        st.push(left / right);
                        break;
                }
            }
            else
            {
                st.push(stoi(str));
            }
        }
        return st.top();
    }
};

在这里插入图片描述

中缀表达式转后缀表达式:
在这里插入图片描述

模拟实现

二者的模拟实现,只能说非常的简单,我们只需要复用vector/list的函数接口即可(其实不严谨,这个问题等会说)。

stack

代码如下:
在这里插入图片描述
和最上面一样的代码,测试:
在这里插入图片描述

queue

在这里插入图片描述

代码:
在这里插入图片描述

前面我说这里直接复用vector和list的函数接口不严谨,现在说一下这个问题。
这里栈和队列的模拟实现,和库中的是不一样的。
我们看一下库中的栈实现的代码:
在这里插入图片描述

其模板参数多了一个Sequence。
这个是干嘛的呢?
其实就是为了将stack搞成适配器。Sequence = deque<T>,这里缺省参数给的是deque,这个也是一个容器,等会再说。

库中stack的成员就一个Sequence c,这个c就相当于我们刚刚实现的vector _con。通过c来实现stack的基本接口,意思就是stack会复用一个默认的容器deque,通过这个deque来实现其基本函数接口,如果你想改变其底层实现stack的容器,就要自己传一个。

测试一下:
在这里插入图片描述
这才叫容器适配器,就是你传过去什么容器,就能用什么容器来实现其所有的功能。只要容器中有你所实现的stack函数接口中的所有功能,就是上面的c.接口。在这里插入图片描述
我们还可以用list来实现:
在这里插入图片描述
这就是适配器。所以我们的代码也是要改一改的。

在这里插入图片描述
这样就和库中的一样了,记得用deque的时候要引对应头文件。

queue也改改:
在这里插入图片描述
但是queue不支持vector来进行实现,因为vector没有头删这个函数接口。

模拟实现就到这,然后说说deque这个容器。

deque

先看文档中的:
在这里插入图片描述
deque的函数接口非常的齐全。
支持头插头删、尾插尾删、任意位置的插入删除、[ ]重载等等。

那么其优势就出来了:

  1. 任意位置插入删除
  2. 支持随机访问
    可以说是list和vector的结合体,但是其也有不好的地方。

这里就要说一下其底层是怎么实现的了。
deque实现,要开好多个小数组buffer,最前面小数组的buffer用来头插,最后小数组的buffer用来尾插,中间的数组就是存放中间数据了,只要一个小数组满了就再开一个小数组,小数组的大小都一样,都是存放n个数据。
还有一个中控数组,是一个指针数组,每个元素指向每一个小数组的首元素地址。

图画出来大概这样:
在这里插入图片描述

假如说其[ ]重载为:operator[](size_t i)
此处假设一个小数组buffer中有8个元素。
其[ ]的原理就是:
(i - 第一个buffer中元素个数) / 8得到该元素在第几个buffer中。
(i - 第一个buffer中元素个数) % 8得到其在这个buffer中是第几个元素。

然后说一下deque的迭代器。
先给张图:
在这里插入图片描述
这里是个略图,右下角是其迭代器,里面有四个指针,说一下各自的意思。

  1. cur指向当前所在的数据位置
  2. first和last表示当前所在buffer的开始和结束
  3. node指向中控数组中的指向当前所在缓冲区的结点指针。

我们看一下其是怎么deque怎么用迭代器遍历。
在这里插入图片描述
对于当前所在buffer,让cur一直++就可以了,等到cur走到last时再++,node就指向下一个buffer的结点指针,然后再让first和last更新为下buffer的头和尾,再让cur指向first就好了。

那么其缺陷就出来了

  1. operator[ ]计算略复杂,大量使用会导致性能下降。
  2. 中间插入删除效率不高。
  3. 底层迭代器会很复杂。

本来deque实现出来就是为了想成为vector和list的结合体的,但是实现出来了后也没有那么厉害,我们可以用一个排序的例子来证明一下:
十万个数:
在这里插入图片描述
可以看到vector是比deque快的。

deque就不讲那么多了,直接给结论:

  1. 头尾插入删除非常合适,相比vector和list而言。很适合做stack和queue的默认适配容器。
  2. 中间插入删除多用list。
  3. 随机访问多用vector。

就这么多,下一篇讲 优先级队列(也是容器适配器)。

到此结束。。。

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

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

相关文章

【动态内存错误详解和C的内存分区】

常见的动态内存错误 1.动态内存错误2.经典案例分析2.1案例一2.1.1**问题分析**2.1.2**修改错误** 2.2案例二2.2.1 原因分析2.2.2 解决问题 c/c内存分布1.2 内存分区简介1.2.1 栈区(stack)1.2.2 堆区(heap)1.2.3 全局(静态)区1.2.4 常量区1.2.5 代码区 1.动态内存错误 &#xf…

DETR (DEtection TRansformer)基于自建数据集开发构建目标检测模型超详细教程

目标检测系列的算法模型可以说是五花八门&#xff0c;不同的系列有不同的理论依据&#xff0c;DETR的亮点在于它是完全端到端的第一个目标检测模型&#xff0c;DETR&#xff08;Detection Transformer&#xff09;是一种基于Transformer的目标检测模型&#xff0c;由Facebook A…

幼儿园门禁安全升级,其实是这么做的!

幼儿园门禁安全是确保幼儿园校园安全的重要方面。为了有效管理出入人员和防止未经授权者进入&#xff0c;幼儿园门禁系统起到了至关重要的作用。 人脸识别门禁系统作为一种先进的技术方案&#xff0c;通过准确识别个体的面部特征&#xff0c;提供了更高的安全性和便捷性。 客户…

HeidiSQL使用

​ 1、点击新建后&#xff0c;选择在根分类创建会话 2、左侧在会话名称下出现的Unnamed&#xff0c;右键选择Rename即可重命名。右侧选择数据库类型&#xff08;mysql&#xff09;&#xff0c;输入主机名&#xff08;默认本机127.0.0.1&#xff09;&#xff0c;用户名&#xff…

webpack打包之 copy-webpack-plugin

copy-webpack-plugin 打包复制文件插件。 1、什么时候要使用&#xff1f; 在离线应用中&#xff0c;前端所有文件都需在在本地&#xff0c;有些文件&#xff08;比如iconFont以及一些静态img)需要转为离线文件&#xff0c;这些文件可以直接引用更方便些&#xff0c;这就需要在打…

0基础小白自学Java“基础语法合集”,新手看这一篇就够了!!

零基础开始学习Java&#xff0c;我们应该如何入手呢&#xff1f;本文将分享以下的6点以帮助朋友们更好的学习。 1、Java注释&#xff1a; 注释是什么&#xff0c;我们为什么要学习注释&#xff1f;注释的分类?注释使用的注意事项? 注释前后对比 注释概念&#xff1a; 注释…

Python基础合集 练习28 (数值运算函数)

from this import d x -120 x的绝对值 x1 abs(x) 同时输出商和余数 y 7 y1 divmod(x1, y) print(y1) /进行幂余运算 z可以省略 (x**y)%z pow(x,y[,z]) pow(3, pow(3, 99), 10000) 四舍五入函数 d是保留小数位数&#xff0c;默认为0 round(x,[,d]) print(round…

什么是JSON

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title> </head> <body> <script>//编写一个JavaScript对象var obj{name:"蒋铭基",id:3,age:19}//将对象转化…

bclinux执行df命令无反应解决方法、bclinux执行df命令卡死解决方法、进程追踪strace命令说明

文章目录 说明解决方法首先排除不是系统问题验证挂载信息追踪df进程strace命令安装strace命令定位df命令卡在哪解决卡住的进程问题解决&#xff0c;验证 说明 额 今天收到反馈&#xff0c;虚拟机执行df命令卡住 卡住状态如下图 解决方法 首先排除不是系统问题 很简单&…

Ceph分布式文件系统——文件系统MDS接口 块存储RDB接口 对象存储RGW接口

创建 CephFS 文件系统 MDS 接口 服务端操作 1&#xff09;在管理节点创建 mds 服务 cd /etc/ceph ceph-deploy mds create node01 node02 node03 2&#xff09;查看各个节点的 mds 服务 ssh rootnode01 systemctl status ceph-mdsnode01 ssh rootnode02 systemctl status ceph…

ERROR 4: Unable to open EPSG support file gcs.csv.

报错&#xff1a; ERROR 4: Unable to open EPSG support file gcs.csv. Try setting the GDAL_DATA environment variable to point to the directory containing EPSG csv files. 解决办法&#xff1a; 将gdal编译文件的data目录设置为用户变量&#xff0c;再运行&#xff…

设计一个web服务器

完整资料进入【数字空间】查看——baidu搜索"writebug" ​ 课程设计目的 《Java&.net》是一门实践性较强的软件基础课程&#xff0c;为了学好这门课程&#xff0c;必须在掌握理论知识的同时&#xff0c;加强上机实践。本课程设计的目的就是要达到理论与实际应用…

前端实现:点击硬币实现硬币翻转动画,且动画停止时正反面随机

html: <div class"pic-box" ref"animationBox"><div class"boxes" click"handleTransform"><div class"box">// 硬币正面图片<img :class"coin1 ? img-pic : img-text" :src"coinPic&…

智能载波远程集抄系统

智能载波远程集抄系统是一种基于物联网技术的先进能源管理系统&#xff0c;它通过利用电力载波通信技术&#xff0c;实现对用户用电数据的远程抄表和监控。该系统不仅可以提高能源管理的效率和精度&#xff0c;同时还可以减少能源浪费和节省成本。 智能载波远程集抄系统主要由三…

Ceph 应用

Ceph 应用 一、创建 CephFS 文件系统 MDS 接口 1.服务端操作 1&#xff09;在管理节点创建 mds 服务 cd /etc/ceph ceph-deploy mds create node01 node02 node032&#xff09;查看各个节点的 mds 服务 ssh rootnode01 systemctl status ceph-mdsnode01 ssh rootnode02 syst…

不满足于RPC,详解Dubbo的服务调用链路

系列文章目录 【收藏向】从用法到源码&#xff0c;一篇文章让你精通Dubbo的SPI机制 面试Dubbo &#xff0c;却问我和Springcloud有什么区别&#xff1f; 超简单&#xff0c;手把手教你搭建Dubbo工程&#xff08;内附源码&#xff09; Dubbo最核心功能——服务暴露的配置、使用…

学习opencv.js(一)

opencv.js是什么 OpenCV.js 是 OpenCV&#xff08;Open Source Computer Vision Library&#xff09;的 JavaScript 版本。OpenCV 是一个广泛使用的计算机视觉和图像处理库&#xff0c;提供了一系列功能强大的算法和工具&#xff0c;用于处理图像、视频、特征提取、对象识别等…

LAXCUS:面向AI的数据计算平台

随着人工智能技术的快速发展&#xff0c;数据计算需求呈现出爆炸式增长。为了满足这一需求&#xff0c;越来越多的企业和研究机构开始寻求更加高效、灵活和可扩展的分布式操作系统。在这个背景下&#xff0c;LAXCUS分布式操作系统应运而生&#xff0c;它是一个面向人工智能的数…

这些会议录音转文字教程还不赶紧学起来?

小芳&#xff1a;嘿&#xff0c;你知道有一些软件可以帮助我们将会议记录中的音频转换成文字吗&#xff1f; 小乐&#xff1a;当然&#xff01;有几种方法可以做到。我们可以直接使用一些音频转文字工具实现。 小芳&#xff1a;那有没有特别推荐的工具&#xff1f; 小乐&…

把一个页面的内容导出为canvas类型的一个图片

效果&#xff1a; 点击即可 下载 打开 得到一个图片 代码&#xff1a; import html2canvas from html2canvas; 弹窗的确认按钮 const handleOk (values) > {visible.value false;printOut(导出告知卡) }; const printOut (name) > {document.body.scrollTop 0; …