C++ stack使用、模拟实现、OJ题

news2025/4/9 5:26:22

目录

一、介绍

二、常用函数

三、模拟实现 

四、OJ练习题

1、最小栈

2、栈的压入、弹出序列

3、逆波兰表达式(后缀转中缀)

4、中缀转后缀思路

5、用栈实现队列


一、介绍

  1. stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行元素的插入与提取操作。
  2. stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部(即栈顶)被压入和弹出。
  3. stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类,这些容器类应该支持以下操作:
    • empty:判空操作
    • back:获取尾部元素操作
    • push_back:尾部插入元素操作
    • pop_back:尾部删除元素操作
  4. 标准容器vector、deque、list均符合这些需求,默认情况下,如果没有为stack指定特定的底层容器,默认情况下使用deque。

二、常用函数

  • stack()   构造空的栈
  • empty()  检测stack是否为空
  • size()      返回stack中元素的个数
  • top()       返回栈顶元素的引用
  • push()    将元素val压入stack中
  • pop()      将stack中尾部的元素弹出
int main() {
    stack<int> myStack; // 创建一个存储int类型的栈对象
    
    // 检测栈是否为空
    cout << "Is stack empty? " << (myStack.empty() ? "Yes" : "No") << endl; 

    myStack.push(10); // 压入元素10
    myStack.push(20); // 压入元素20
    myStack.push(30); // 压入元素30

    // 输出栈中元素的个数
    cout << "Stack size: " << myStack.size() << endl; 
    
    // 输出栈顶元素的值
    cout << "Top element: " << myStack.top() << endl;

    // 弹出栈顶元素
    myStack.pop(); 
    
    // 输出弹出后的栈顶元素的值
    cout << "Top element after pop: " << myStack.top() << endl; 

    return 0;
}

 

三、模拟实现 

template<class T, class Container = vector<T>>
class stack
{
public:
	void push(const T& x)
	{
		_con.push_back(x);
	}

	void pop()
	{
		_con.pop_back();
	}

	const T& top()
	{
		return _con.back();
	}

	size_t size()
	{
		return _con.size();
	}

	bool empty()
	{
		return _con.empty();
	}

private:
	Container _con;
};

这段代码实现了一个栈(stack)类模板,使用适配器模式(Adapter Pattern)来适配不同类型的容器(Container)作为底层实现。

适配器模式是一种结构型设计模式,用于将一个类的接口转换成客户端所期望的另一个接口。在这个例子中,stack类的接口被适配成了与Container类型兼容的接口。

  • 模板类stack有两个模板参数:T表示栈中元素的类型,Container表示底层容器的类型,默认为vector。这样,我们可以通过指定不同的容器类型来实现不同的底层实现。
  • stack类提供了一些常见的栈操作函数,包括push、pop、top、size和empty。这些函数在内部通过调用底层容器的相应函数来实现栈的功能。
  • 例如,push函数将元素添加到底层容器的末尾,pop函数从底层容器的末尾移除元素,top函数返回底层容器的末尾元素,size函数返回底层容器中元素的个数,empty函数检查底层容器是否为空。
  • 通过使用适配器模式,我们可以将不同类型的容器(如vector、list、deque等)适配成具有相同接口的栈类,从而实现了代码的复用和灵活性。

四、OJ练习题

1、最小栈

这个问题的关键在于如何在常数时间内检索到最小元素。为了实现这个目标,我们可以使用两个栈:一个栈 _st 用于正常的 push 和 pop 操作,另一个栈 _minst 用于存储当前栈中的最小元素。

class MinStack {
public:
    MinStack() {}

    void push(int val) {
        _st.push(val);
        if (_minst.empty() || val <= _minst.top())
        {
            _minst.push(val);
        }
    }

    void pop() {
        if (_minst.top() == _st.top()) {
            _minst.pop();
        }
        _st.pop();
    }

    int top() {
        return _st.top();
    }

    int getMin() {
        return _minst.top();
    }

private:
    stack<int> _st;
    stack<int> _minst;
};

详细步骤:

  1. 初始化:我们初始化两个空栈 _st 和 _minst

  2. push 操作:当我们将一个元素 val 推入栈 _st 时,我们也需要检查它是否应该被推入栈 _minst。如果 _minst 为空,或者 val 小于等于 _minst 的栈顶元素,那么我们就将 val 推入 _minst。这样,_minst 的栈顶元素就始终是 _st 中的最小元素。

  3. pop 操作:当我们从 _st 中弹出一个元素时,我们需要检查它是否是 _minst 的栈顶元素。如果是,那么我们也需要从 _minst 中弹出它,因为这个元素已经不再 _st 中了,所以 _minst 的栈顶元素需要更新。

  4. top 操作:这个操作只需要返回 _st 的栈顶元素。

  5. getMin 操作:这个操作只需要返回 _minst 的栈顶元素,因为我们已经确保了 _minst 的栈顶元素始终是 _st 中的最小元素。

这个算法的关键在于,我们使用了一个额外的栈 _minst 来存储当前栈中的最小元素,这样我们就可以在常数时间内检索到最小元素。

2、栈的压入、弹出序列

这个问题的关键在于理解栈的特性,即后进先出(LIFO)。我们可以通过一个辅助栈来模拟压入和弹出的过程,以此来判断给定的弹出序列是否可能。

class Solution {
  public:
    bool IsPopOrder(vector<int>& pushV, vector<int>& popV) {
        stack<int> st;
        int pushi = 0, popi = 0;
        while (pushi < pushV.size()) {
            st.push(pushV[pushi++]);
            while (!st.empty() && st.top() == popV[popi]) {
                st.pop();
                popi++;
            }
        }
        return popi==popV.size();
    }
};

  • 初始化一个空栈,用于模拟压入和弹出的过程。同时,初始化两个指针,pushi 和 popi,分别指向 pushV 和 popV 的开始位置。

  • 当 pushi 小于 pushV 的大小时,执行以下操作:

    • 将 pushV[pushi] 压入栈中,并将 pushi 加一。
    • 检查栈顶元素是否等于 popV[popi]。如果相等,说明当前栈顶元素是下一个要弹出的元素,因此我们将其从栈中弹出,并将 popi 加一。我们需要不断进行这个检查,直到栈为空或者栈顶元素不等于 popV[popi]
  • 最后,如果 popi 等于 popV 的大小,说明 popV 中的所有元素都正确地被弹出了栈,因此返回 true。否则,返回 false

这个算法的关键在于,它利用了栈的特性来模拟了压入和弹出的过程。当栈顶元素等于 popV[popi] 时,我们知道这个元素应该被弹出,因为在给定的弹出序列中,它是下一个要弹出的元素。

3、逆波兰表达式(后缀转中缀)

思路:

  1. 遇到操作数入栈。
  2. 遇到操作符,取栈顶的两个操作数进行运算,运算结果重新入栈。
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、中缀转后缀思路

1、操作数输出

2、操作符

        (1)栈为空进栈

        (2)栈不为空,跟栈顶的操作符比较。

                a、比栈顶操作符优先级高,进栈。

                b、比栈顶优先级低或相等,出栈顶操作符输出。

带括号的中缀转后缀 

1 + 2*(4 - 5) + 6 /7  >>  1245-*+67/+ 

  1. ( ) 优先级最低
  2. ( 不比较直接入栈
  3. ) 参与比较,直接遇到 (

5、用栈实现队列

 

 

class MyQueue {
public:
    MyQueue() {}

    void push_to_pop(){
        if(stpop.empty()){
            while (!stpush.empty()) {
                stpop.push(stpush.top());
                stpush.pop();
            }
        }
    }

    void push(int x) { stpush.push(x); }

    int pop() {
        push_to_pop();
        int x=stpop.top();
        stpop.pop();
        return x;
    }

    int peek() {
        push_to_pop();
        return stpop.top();
    }

    bool empty() { return stpush.empty() && stpop.empty(); }

private:
    stack<int> stpush;
    stack<int> stpop;
};

/**
 * 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();
 */
  1. 首先定义了一个名为MyQueue的类,表示队列。
  2. 类中有两个私有成员变量stpushstpop,它们分别表示用于入队和出队操作的两个栈。
  3. MyQueue类中定义了一个默认构造函数MyQueue(),用于创建一个空的队列。
  4. push_to_pop()函数用于将stpush栈中的元素转移到stpop栈中。如果stpop栈为空,则将stpush栈中的元素逐个弹出并压入stpop栈中,以实现队列的先进先出顺序。
  5. push(int x)函数用于将元素x入队,即将元素压入stpush栈中。
  6. pop()函数用于出队操作,即从队列中移除并返回队头元素。在执行出队操作之前,首先调用push_to_pop()函数,确保stpop栈中有元素。然后从stpop栈中弹出栈顶元素,并返回该元素。
  7. peek()函数用于获取队头元素,但不对队列进行修改。同样,在执行获取队头元素操作之前,先调用push_to_pop()函数,确保stpop栈中有元素。然后返回stpop栈的栈顶元素。
  8. empty()函数用于判断队列是否为空。当stpushstpop两个栈都为空时,队列为空,返回true;否则返回false

这样,通过使用两个栈,我们可以实现队列的基本功能,包括入队、出队、获取队头元素和判断队列是否为空。

在代码的最后,给出了使用MyQueue类的示例代码。首先创建一个MyQueue对象obj,然后可以通过调用obj的成员函数来进行入队、出队、获取队头元素和判断队列是否为空的操作。

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

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

相关文章

vr体验馆用什么软件计时计费,如遇到停电软件程序如何恢复时间

vr体验馆用什么软件计时计费&#xff0c;如遇到停电软件程序如何恢复时间 一、软件程序问答 如下图&#xff0c;软件以 佳易王vr体验馆计时计费软件V17.9为例说明 1、软件如何计时间&#xff1f; 点击相应编号的开始计时按钮即可 2、遇到停电再打开软件时间可以恢复吗&…

谷歌开发者账号:企业号和个人号的区别与优劣势对比

根据近期谷歌开发者账号的热点和测试情况&#xff0c;与大家探讨一下企业号和个人号的区别和优劣势对比&#xff0c;以及后续可能的发展方向。 个人号问题分析 由于过去个人号的滥用行为&#xff0c;谷歌采取了多项风险控制措施&#xff0c;这些措施包括了对注册地区进行限制&a…

vue3项目使用pako库解压后端返回zip数据

文章目录 前言一、pako 介绍一些特点和功能&#xff1a;简单示例 二、vue3 实战示例1.安装后引入库安装:引用用自定义hooks 抽取共用逻辑部署小插曲 前言 外部接口返回一个图片数据是经过zip压缩的&#xff0c;前端需要把这个数据处理成可以显示的图片。大概思路&#xff1a;z…

thinkphp学习01-thinkphp6安装

thinkphp官网 thinkphp文档 准备 安装php 安装composer 创建项目 切换到目录下&#xff0c;新建项目&#xff0c;通过composer创建 composer create-project topthink/think tp6启动 命令行启动 进入到tp6文件夹&#xff0c;执行启动命令 php think run访问localhost:8…

状态模式-举例

在软件系统中&#xff0c;有些对象也像水一样具有多种状态&#xff0c; 这些状态在某些情况下能够相互转换&#xff0c; 而且对象在不同的状态下也将具有不同的行为。 参考日志来设置状态。 如何判断一个设计模式是行为模式还是什么其他模式&#xff1f; 什么叫行为模式&#…

山西电力市场日前价格预测【2023-12-28】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2023-12-28&#xff09;山西电力市场全天平均日前电价为814.30元/MWh。其中&#xff0c;最高日前电价为1500.00元/MWh&#xff0c;预计出现在08:00~08:45,17:00~20:15。最低日前电价为394.61元/…

FFmpeg学习笔记--Centos8安装FFmpeg

1--安装指令 sudo yum install epel-releasesudo yum localinstall --nogpgcheck https://download1.rpmfusion.org/free/el/rpmfusion-free-release-8.noarch.rpmsudo yum install ffmpeg ffmpeg-develffmpeg -version 2--版本信息

设计模式-注册模式

设计模式专栏 模式介绍模式特点应用场景注册模式和单例模式的区别代码示例Java实现注册模式Python实现注册模式 注册模式在spring中的应用 模式介绍 注册模式是一种设计模式&#xff0c;也称为注册树或注册器模式。这种模式将类的实例化和创建分离开来&#xff0c;避免在应用程…

Linux文件的扩展属性 attr cap

文件属性 Linux文件属性分为常规属性与扩展属性&#xff0c;其中扩展属性有两种&#xff1a;attr与xattr. 一般常规的文件属性由stat API 读取&#xff0c;一般是三种权限&#xff0c;ower, group&#xff0c;时间等。 扩展属性attr 用户态API ioctl(fd, FS_IOC32_SETFLAGS…

shiro1.10版本后-IniSecurityManagerFactory过期失效

1、问题概述&#xff1f; 今天在研究了shiro的新版本shiro1.13.0版本&#xff0c;发现用了很长时间的IniSecurityManagerFactory工厂失效了。 从下图中可以看出&#xff0c;在新版本中IniSecurityManagerFactory被打上了过期线了。 那么问题来了&#xff0c;新版本如何使用呢…

数据压缩专题——静止图像的小波变换编码

随着数字图像技术的发展和应用的广泛&#xff0c;对图像的压缩和编码变得越来越重要。小波变换编码作为一种有效的图像压缩和编码方法&#xff0c;在静止图像处理中得到了广泛应用。本文将介绍静止图像的小波变换编码的基本原理和关键步骤&#xff0c;以及其在图像压缩中的应用…

1panel使用指南(一)面板安装

一、1panel简介 1Panel是杭州飞致云信息科技有限公司推出的产品 [1]&#xff0c;帮助用户实现快速建站。 [2]是一款现代化、开源的Linux服务器运维管理面板&#xff0c;于2023年3月推出&#xff0c;深度集成WordPress和Halo&#xff0c;一键完成域名绑定、SSL证书配置等操作&a…

用linux中定时任务Crontab,向企业微信群通过机器人发送消息

1.使用yum命令安装Crontab&#xff1a;这个很关键&#xff0c;没有安装的话会提示命令not found yum install vixie-cron yum install crontabs 注&#xff1a;vixie-cron软件包是cron的主程序&#xff1b; crontabs软件包是用来安装、卸装、或列举用来驱动 cron 守护进程的表…

Hadoop(2):常见的MapReduce[在Ubuntu中运行!]

1 以词频统计为例子介绍 mapreduce怎么写出来的 弄清楚MapReduce的各个过程&#xff1a; 将文件输入后&#xff0c;返回的<k1,v1>代表的含义是&#xff1a;k1表示偏移量&#xff0c;即v1的第一个字母在文件中的索引&#xff08;从0开始数的&#xff09;&#xff1b;v1表…

C#中使用is关键字检查对象是否与给定类型兼容

目录 一、定义 二、示例 三、生成 在程序的开发过程中经常会使用类型转换&#xff0c;如果类型转换不成功则会出现异常&#xff0c;从抛出异常到捕获并处理异常&#xff0c;无形中增加了系统的开销&#xff0c;而且太过频繁地处理异常还会严重地影响系统的稳定性。is关键字可…

要学习openfoam,c++需要掌握到什么程度?

要学习openfoam&#xff0c;c需要掌握到什么程度&#xff1f; 在开始前我有一些资料&#xff0c;是我根据自己从业十年经验&#xff0c;熬夜搞了几个通宵&#xff0c;精心整理了一份「c的资料从专业入门到高级教程工具包」&#xff0c;点个关注&#xff0c;全部无偿共享给大家&…

JY901S 9轴姿态角度传感器模块

JY901S 9轴姿态角度传感器模块 JY901S 简介模块特性引脚说明IIC通讯IIC读写寄存器代码示例 JY901S 简介 模块集成高精度的陀螺仪、加速度计、地磁场传感器&#xff0c;采用高性能的微处理器和先进的动力学解算与卡尔曼动态滤波算法&#xff0c;能够快速求解出模块当前的实时运…

PHP序列化总结2--常见的魔术方法

魔术方法的概念 PHP的魔术方法是一种特殊的方法&#xff0c;用于覆盖PHP的默认操作。它们以双下划线&#xff08;__&#xff09;开头&#xff0c;后面跟着一些特定的字符串&#xff0c;如__construct()、__destruct()、__get()等。这些魔术方法在对象执行特定操作时被自动调用…

OpenCV-Python(22):直方图的计算绘制与分析

目标 了解直方图的原理及应用使用OpenCV 或Numpy 函数计算直方图使用Opencv 或者Matplotlib 函数绘制直方图学习函数cv2.calcHist()、np.histogram()等 原理及应用 直方图是一种统计图形&#xff0c;是对图像的另一种解释&#xff0c;用于表示图像中各个像素值的频次分布。直…

【Linux Shell学习笔记】Linux Shell的位置参数与函数

一、位置参数 位置参数&#xff0c;也被称之为位置变量&#xff0c;通过位置参数&#xff0c;可以在执行程序的时候&#xff0c;向程序传递数据 1.1 shell接收参数的方法 1.2 向shell传递参数的方法 二、函数 2.1 函数基础 2.1.1 函数简介 函数本质上就是一个代码块&#xf…