【C++从0到王者】第十六站:stack和queue的使用

news2024/11/25 12:32:29

文章目录

  • 一、stack的使用
    • 1.stack的介绍
    • 2.stack的使用
  • 二、queue的使用
    • 1.queue的护额晒
    • 2.queue的使用
  • 三、stack和queue相关算法题
    • 1.最小栈
    • 2.栈的压入、弹出序列
    • 3.逆波兰表达式
    • 4.两个栈实现一个队列
    • 5.用两个队列实现栈
    • 6.二叉树的层序遍历
      • 1.双队列
      • 2.用一个变量levelSize去控制
    • 7.二叉树的层序遍历Ⅱ

一、stack的使用

1.stack的介绍

在这里插入图片描述

栈是一种容器适配器,专门设计用于在 LIFO 上下文中操作(后进先出),其中元素只从容器的一端插入和提取。

栈被实现为容器适配器,这些类使用特定容器类的封装对象作为其底层容器,提供一组特定的成员函数来访问其元素。 元素从特定容器的“后端”被推入/弹出,这被称为栈的顶部。

底层容器可以是任何标准的容器类模板,也可以是其他一些专门设计的容器类。容器应该支持以下操作:empty、size、back、push_back、pop_front

标准容器类 vector、deque 和 list 满足这些要求。默认情况下,如果没有为特定的堆栈类实例化指定容器类,则使用标准容器 deque。

这里我们需要知道的是,Container适配器就是一个现有的容器进行的一个转换
也就是说,适配器的本质就是一种复用

2.stack的使用

我们先看stack的接口有哪些
在这里插入图片描述

如上所示,这里其实我们一看就已经猜出了七七八八了。因为与前面是string、vector、list是十分相似的。只要结合它的先进先出的特性,我们就知道每个函数都是什么意思了。

对于stack的使用是非常简单的

void test_stack()
{
	stack<int> st1;
	st1.push(1);
	st1.push(2);
	st1.push(3);
	st1.push(4);
	while (!st1.empty())
	{
		cout << st1.top() << " ";
		st1.pop();
	}
	cout << endl;
}

在这里插入图片描述

二、queue的使用

1.queue的护额晒

在这里插入图片描述

队列是一种容器适配器,专门设计用于在FIFO上下文中操作(先进先出),其中将元素插入容器的一端并从另一端提取。

队列是作为容器适配器实现的,容器适配器是使用特定容器类的封装对象作为其底层容器的类,提供一组特定的成员函数来访问其元素。元素被推入特定容器的“后面”,并从其“前面”弹出。

底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。该底层容器应至少支持以下操作:empty、size、front、back、push_back、pop_back。

标准容器类deque和list满足这些要求。默认情况下,如果没有为特定队列类实例化指定容器类,则使用标准容器队列。

2.queue的使用

同样的,我们先来看queue的接口有哪些
在这里插入图片描述

在这些接口中,和stack是类似的,只要我们知道了queue的特性是先进先出。我们就能很轻松的推测出每个接口的意思

对于queue的使用是很简单的

void test_queue()
{
	queue<int> q;
	q.push(1);
	q.push(2);
	q.push(3);
	q.push(4);
	while (!q.empty())
	{
		cout << q.front() << " ";
		q.pop();
	}
	cout << endl;
}

在这里插入图片描述

三、stack和queue相关算法题

1.最小栈

题目链接:最小栈

题目解析:在这道题中,我们为了模拟这种情况,我们可以在成员变量中定义两个栈,一个栈作为正常的出入数据使用,一个栈用来存储最小值。
首先是构造函数,由于我们的成员变量都是自定义类型,所以会自动调用他们的默认构造函数,即他们也会走初始化列表,所以默认构造函数我们是可以直接不用管的。甚至于我们可以直接删掉题目给的构造函数,因为我们不写,编译器自己生成一个。
其次我们的大逻辑是这样的,当我们最小栈为空的时候,我们的最小栈是需要入一个数据的,当我们将要插入的元素是小于等于最小栈要插入的元素的时候,我们会将这个元素给入最小栈
当我们pop的时候,我们也是同理的,如果我们要删除的数据等于最小栈的栈顶元素,那么就也要删除最小栈的栈顶元素。

class MinStack {
public:
    MinStack()
    {}
    
    void push(int val) {
        st.push(val);
        if(min.empty()||(val<=min.top()))
        {
            min.push(val);
        }
    }
    
    void pop() {
        int val=st.top();
        st.pop();
        if(val==min.top())
        {
            min.pop();
        }
    }
    
    int top() {
        return st.top();
    }
    
    int getMin() {
        return min.top();
    }
private:
    stack<int> st;
    stack<int> min;
};

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack* obj = new MinStack();
 * obj->push(val);
 * obj->pop();
 * int param_3 = obj->top();
 * int param_4 = obj->getMin();
 */

2.栈的压入、弹出序列

题目链接:栈的压入弹出序列

题目解析:我们可以使用一个栈来模拟它的入栈出栈逻辑,只要顺着它的思路最终我们的这个栈是空栈的话,那么就说明是匹配的,否则不匹配

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param pushV int整型vector 
     * @param popV int整型vector 
     * @return bool布尔型
     */
    bool IsPopOrder(vector<int>& pushV, vector<int>& popV) {
        // write code here
        stack<int> st;
        int n=pushV.size();
        int j=0;
        for(int i=0;i<n;i++)
        {
            st.push(pushV[i]);
            while(!st.empty() && st.top()==popV[j])
            {
                st.pop();
                j++;
            }            
        }
        return st.empty();
    }
};

3.逆波兰表达式

题目链接:逆波兰表达式

再谈这道题之前,我们应该先知道什么是逆波兰表达式,我们正常的都是中缀表达式,即3+2*5这种的,都被称之为中缀表达式。

而中缀表达式在计算机中是很难进行运算的。我们需要先将其转换为后缀表达式,前文所说的中缀表达式转化为后缀表达式后应该为3 2 5 * +。后缀表达式的特点就是操作数的顺序不变,而操作符的顺序按照优先级进行了重排。

我们先来看一下后缀运算符是如何进行运算的:

  1. 操作数入栈
  2. 如果是操作符,取出栈顶的两个元素进行计算,计算结果放入栈中

在这里插入图片描述

那么如何使得中缀转为后缀呢?

  1. 操作数输出(即将操作数放到一个容器中)
  2. 操作符入栈 : ①栈为空,当前操作符比栈顶的优先级高,继续入栈 ②栈不为空,且当前操作符比栈顶的优先级低或者相等,则输出栈顶操作符(因为运算符的优先级只与它相邻的操作符有关,是相对的,如果后面出现了一个更高的操作符,我们无法确定后面是否还有更高的操作符,反而是如果有一个相对较低的操作符,那么前两个肯定是可以进行运算的)
  3. 表达式结束后,依次出栈顶的操作符
  4. 注意,有可能会在转换的中间出现连续出操作符的情况,即栈里面已经存储了好几个运算符了,下面的一个运算符要比好几个都要低,就要连续出好几个运算符

比如说2+4-1*3,这个中缀表达式,按照上面的规则可以化为
在这里插入图片描述

上面都是正常情况的下的处理,但是还有时候会出现括号的影响。

这里可以考虑加上一个特殊标记,当我们这个标记生效时,就代表进入括号内了。或者在这里走一个递归也是可以的。递归的方法就是在遇到括号的时候,我们将括号里面的运算符就需要放入一个新的栈中了。相当于我们只需要让括号返回一个结果就可以了。但是数据还是输出在原来的顺序表中的

我们在回过头来看这道题,我们有了上面的分析,就很容易写出下面代码了。

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();
    }
};

4.两个栈实现一个队列

题目链接:两个栈实现一个队列

对于这个题,我们在之前也已经做过一次分析了,只不过上一次是用C语言手撕了一个栈来实现了。而现在呢,我们有C++的库了,因此我们就可以直接使用C++的库来完成这件事。

class MyQueue {
public:
    MyQueue() {}
    
    void push(int x) {
        _push.push(x);
    }
    
    int pop() {
        if(_pop.empty())
        {
            while(!_push.empty())
            {
                int val=_push.top();
                _push.pop();
                _pop.push(val);
            }
        }
        int val=_pop.top();
        _pop.pop();
        return val;
    }
    
    int peek() {
        if(_pop.empty())
        {
            while(!_push.empty())
            {
                int val=_push.top();
                _push.pop();
                _pop.push(val);
            }
        }
        return _pop.top();
    }
    
    bool empty() {
        return (_push.empty()&&_pop.empty());
    }
private:
    stack<int> _push;
    stack<int> _pop;
};

/**
 * Your MyQueue object will be instantiated and called as such:
 * MyQueue* obj = new MyQueue();
 * obj->push(x);
 * int param_2 = obj->pop();
 * int param_3 = obj->peek();
 * bool param_4 = obj->empty();
 */

5.用两个队列实现栈

题目链接:用两个队列实现栈

这道题与上面的题类似,我们也是曾经使用C语言做过,不过由于C语言没有轮子,就需要我们自己造轮子,有了很多的麻烦。

class MyStack {
public:
    MyStack() {}
    
    void push(int x) {
        if(q1.empty())
        {
            q2.push(x);
        }
        else
        {
            q1.push(x);
        }
    }
    
    int pop() {
        if(q1.empty())
        {
            while(q2.size()>1)
            {
                int val=q2.front();
                q2.pop();
                q1.push(val);
            }
            int val=q2.front();
            q2.pop();
            return val;
        }
        else
        {
            while(q1.size()>1)
            {
                int val=q1.front();
                q1.pop();
                q2.push(val);
            }
            int val=q1.front();
            q1.pop();
            return val;
        }
        
    }
    
    int top() {
        if(q1.empty())
        {
            return q2.back();
        }
        else
        {
            return q1.back();
        }
    }
    
    bool empty() {
        return (q1.empty()&&q2.empty());
    }
private:
    queue<int> q1;
    queue<int> q2;
};

/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack* obj = new MyStack();
 * obj->push(x);
 * int param_2 = obj->pop();
 * int param_3 = obj->top();
 * bool param_4 = obj->empty();
 */

6.二叉树的层序遍历

题目链接:二叉树的层序遍历

对于这道题,如果不是用C++来完成的话,用C去完成的话是非常难的。不仅我们要确保我们造的轮子是正确的,而且还有很多细节需要进行处理,但我们如果使用C++的话可以极大的简化很多操作

1.双队列

我们可以使用两个队列去完成这件事,一个队列用来存储结点指针,一个队列用来存储该节点处于哪个层。这样我们就可以知道哪个结点是那个层的了,自然我们就很容易的得知层序遍历了。

2.用一个变量levelSize去控制

这种思路是比较奇妙的,我们使用一个变量来确定当前该层有多少个结点,用一个队列来存储结点,然后就是层序遍历的基本套路,每出一个结点,带来两个孩子。将每一层的数据存储在一个vector中,然后一层结束后将这一层的vector插入到vector<vector<int>>中。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        queue<TreeNode*> q;
        int levelSize=0;
        if(root)
        {
            q.push(root);
            levelSize=1;
        }
        vector<vector<int>> vv;
        while(!q.empty())
        {
            vector<int> v;
            for(int i=0;i<levelSize;i++)
            {
                TreeNode* front=q.front();
                q.pop();
                v.push_back(front->val);
                if(front->left)
                {
                    q.push(front->left);
                }
                if(front->right)
                {
                    q.push(front->right);
                }
            }
            vv.push_back(v);
            levelSize=q.size();
        }
        return vv;
    }
};

7.二叉树的层序遍历Ⅱ

题目链接:二叉树的层序遍历Ⅱ

对于这道题目,我们可以注意到他是让倒着遍历的。和前一道题基本是一样的,只是改变了vv的顺序。即相当于将vv给逆序。那么这道题就太简单了,直接将前面这道题给拷贝过来,然后调用库里面的reverse即可

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> levelOrderBottom(TreeNode* root) {
        queue<TreeNode*> q;
        int levelSize=0;
        if(root)
        {
            q.push(root);
            levelSize=1;
        }
        vector<vector<int>> vv;
        while(!q.empty())
        {
            vector<int> v;
            for(int i=0;i<levelSize;i++)
            {
                TreeNode* front=q.front();
                q.pop();
                v.push_back(front->val);
                if(front->left)
                {
                    q.push(front->left);
                }
                if(front->right)
                {
                    q.push(front->right);
                }
            }
            vv.push_back(v);
            levelSize=q.size();
        }
        reverse(vv.begin(),vv.end());
        return vv;
    }
};

好了,本期内容就到这里了
如果对你有帮助的话,不要忘记点赞加收藏哦!!!

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

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

相关文章

解决 “fatal: Could not read from remote repository.

问题描述&#xff1a; 在使用Git将本地仓库推送到远程仓库或将远程仓库克隆到本地的时候&#xff0c;发生了如下错误&#xff1a;“fatal: Could not read from remote repository.” 原因分析&#xff1a; 出现这错误一般是以下两种原因&#xff1a; 客户端与服务端未生成 …

详解Kafka分区机制原理|Kafka 系列 二

Kafka 系列第二篇&#xff0c;详解分区机制原理。为了不错过更新&#xff0c;请大家将本号“设为星标”。 点击上方“后端开发技术”&#xff0c;选择“设为星标” &#xff0c;优质资源及时送达 上一篇文章介绍了 Kafka 的基本概念和术语&#xff0c;里面有个概念是 分区(Part…

音视频技术开发周刊 | 305

每周一期&#xff0c;纵览音视频技术领域的干货。 新闻投稿&#xff1a;contributelivevideostack.com。 大神回归学界&#xff1a;何恺明宣布加入 MIT 「作为一位 FAIR 研究科学家&#xff0c;我将于 2024 年加入麻省理工学院&#xff08;MIT&#xff09;电气工程与计算机科学…

LeetCode 130. 被围绕的区域

题目链接&#xff1a;130. 被围绕的区域 题目描述 给你一个 m x n 的矩阵 board &#xff0c;由若干字符 ‘X’ 和 ‘O’ &#xff0c;找到所有被 ‘X’ 围绕的区域&#xff0c;并将这些区域里所有的 ‘O’ 用 ‘X’ 填充。 示例1&#xff1a; 输入&#xff1a;board [[“…

论文阅读 RRNet: A Hybrid Detector for Object Detection in Drone-captured Images

文章目录 RRNet: A Hybrid Detector for Object Detection in Drone-captured ImagesAbstract1. Introduction2. Related work3. AdaResampling4. Re-Regression Net4.1. Coarse detector4.2. Re-Regression 5. Experiments5.1. Data augmentation5.2. Network details5.3. Tra…

线性代数(二) 矩阵及其运算

前言 行列式det(A) 其实表示的只是一个值 ∣ a b c d ∣ a d − b c \begin{vmatrix} a & b\\ c & d\end{vmatrix} ad -bc ​ac​bd​ ​ad−bc&#xff0c;其基本变化是基于这个值是不变。而矩阵表示的是一个数表。 定义 矩阵与线性变换的关系 即得 ( a 11 a 12…

(el-switch)操作:Element-plus 中 Switch 将默认值修改为 “true“ 与 “false“(字符串)来控制开关

Ⅰ、Element-plus 提供的 Switch 开关组件与想要目标情况的对比&#xff1a; 1、Element-plus 提供 Switch 组件情况&#xff1a; 其一、Element-ui 自提供的 Switch 代码情况为(示例的代码)&#xff1a; // Element-plus 自提供的代码&#xff1a; // 此时是使用了 ts 语言环…

英语使用场景口语

HOTEL ENGLISH hotel motel inn b&b Process 1.booking a room can i reserve a room? reservation do you have and singles? double room standard room deluxe room presidential suite do you have a pick-up service? 2.checking in where is the recept…

C++物理引擎Box2D的下载,编译,VS2013配置环境

文章目录 网站和下载地址编译工具:编译box2dhelloworld测试网站和下载地址 https://box2d.org/ 下载地址 https://hub.nuaa.cf/erincatto/box2d/tags 编译工具: 1.VS2013 2.cmake 下载地址 https://cmake.org/ 编译box2d 下载box2d源码2.4.0,解压。在box2d-2.4.0目录下…

手机便签内容不见了怎么恢复正常?

在日常生活和工作中&#xff0c;很多人都需要随手记录事情&#xff0c;例如家庭琐事、孩子相关的事情、指定时间需要完成的工作任务、会议安排等。当我们需要随时随地记录事情的时候&#xff0c;手机便签应用就是非常不多的选择&#xff0c;我们直接打开手机上的便签APP就可以新…

浏览器无法连接网络问题

问题描述 电脑其他程序都能正常联网&#xff0c;但是所有的浏览器都无法联网&#xff0c;同时外部网站都能ping通 问题诊断 查看电脑Internet连接的问题报告显示&#xff1a;该设备或资源(Web 代理)未设置为接受端口"7890"上的连接。 解决方案 经过检查发现不是IP地址…

QT自带PDF库的使用

QT自带PDF库可以方便的打开PDF文件&#xff0c;并将文件解析为QImage&#xff0c;相比网上提供的开源库&#xff0c;QT自带PDF库使用更方便&#xff0c;也更加可靠&#xff0c;然而&#xff0c;QT自带PDF库的使用却不同于其他通用库的使用&#xff0c;具备一定的技巧。 1. 安装…

Namecheap 便宜域名注册使用,直接购买

FREENOM免费域名不能注册了&#xff0c;现在只能自己动手注册便宜的域名&#xff0c;前面我们也记录了不能注册FREENOM免费域名不能注册怎么办&#xff0c;不能注册FREENOM免费域名&#xff0c;怎么办&#xff0c;这里是解决方案&#xff01; 注册6元域名。 现在我们又多了一个…

机器学习深度学习——文本预处理

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位即将上大四&#xff0c;正专攻机器学习的保研er &#x1f30c;上期文章&#xff1a;机器学习&&深度学习——序列模型&#xff08;NLP启动&#xff01;&#xff09; &#x1f4da;订阅专栏&#xff1a;机器学习&am…

Blazor前后端框架Known-V1.2.11

V1.2.11 Known是基于C#和Blazor开发的前后端分离快速开发框架&#xff0c;开箱即用&#xff0c;跨平台&#xff0c;一处代码&#xff0c;多处运行。 Gitee&#xff1a; https://gitee.com/known/KnownGithub&#xff1a;https://github.com/known/Known 概述 基于C#和Blazo…

运维作业—5

一.基于 CentOS 7 构建 LVS-DR 群集 1.配置LVS 2.第一台real server&#xff08;192.168.100.139:80&#xff09; 手工在RS端绑定VIP 手工在RS端抑制ARP响应 3.第二台real server&#xff08;192.168.100.140:80&#xff09; 安装arptables并启动 使用arptables实现抑制 测试…

网卡内部的 DMA

前言 MCU、SOC 内部通常带有 DMA 控制器&#xff0c;要想使用 DMA 通常需要如下操作 选择通道配置传输方向&#xff08;内存到外设、内存到内存、外设到内存&#xff09;设置源地址、目的地址&#xff08;内存地址、外设地址&#xff09;设置源地址、目的地址是否自增设置位宽…

【Spring Cloud 】基于微服务架构的智慧工地监管平台源码带APP

智慧工地监管平台是一种利用物联网、云计算、大数据等技术手段实现工地信息化管理的解决方案。它通过数据采集、分析和应用&#xff0c;在实时监控、风险预警、资源调度等方面为工地管理者提供了全方位的支持&#xff0c;提高了工地管理的效率和质量。智慧监管平台还基于“云端…

Detector定位算法在FPGA中的实现——section1 原理推导

关于算法在FPGA中的实现&#xff0c;本次利用业余的时间推出一个系列章节&#xff0c;专门记录从算法的推导、Matlab的实现、FPGA的移植开发与仿真做一次完整的FPGA算法开发&#xff0c;在此做一下相关的记录和总结&#xff0c;做到温故知新。 这里以Detector在Global Coordina…

C++11 新特性 ---- 模板的优化

C11 模板机制:① 函数模板② 类模板模板的使用&#xff1a;① 范围&#xff1a;模板的声明或定义只能在全局或类范围进行&#xff0c;不可以在局部范围&#xff08;如函数&#xff09;② 目的&#xff1a;为了能够编写与类型无关的代码函数模板&#xff1a;- 格式&#xff1a;t…