【优选算法 | 栈】深入栈模拟题:从题型特征到实现技巧

算法 相关知识点 可以通过点击 以下链接进行学习 一起加油!
双指针 滑动窗口 二分查找 前缀和 位运算
模拟 链表 哈希表 字符串模拟

在算法学习中,栈是最基础也是最容易上手的数据结构之一。然而,当它被用于模拟复杂操作流程时,却常常成为区分"写得出"和"写得好"之间的分水岭。

本文将精选常见的栈模拟题,系统分析它们的思维模式与代码实现,带你掌握这类题目的出题套路与解题技巧。

🌈个人主页:是店小二呀

🌈C/C++专栏:C语言\ C++

🌈初/高阶数据结构专栏: 初阶数据结构\ 高阶数据结构

🌈Linux专栏: Linux

🌈算法专栏:算法

🌈Mysql专栏:Mysql

🌈你可知:无人扶我青云志 我自踏雪至山巅

文章目录

    • [1047. 删除字符串中的所有相邻重复项](#1047. 删除字符串中的所有相邻重复项)
    • [844. 比较含退格的字符串](#844. 比较含退格的字符串)
    • [227. 基本计算器 II](#227. 基本计算器 II)
    • [394. 字符串解码](#394. 字符串解码)
    • [946. 验证栈序列](#946. 验证栈序列)

1047. 删除字符串中的所有相邻重复项

【题目 】:1047. 删除字符串中的所有相邻重复项

【算法思路】

本题与我们玩过的「开心消消乐」游戏非常相似。仔细观察消除过程,我们可以发现,本题与「括号匹配」问题类似。当前元素是否被消除,需要依赖上一个元素的信息,因此可以使用「栈」来存储信息。

但是,使用 stack 来保存信息的话,最终还需要将结果从栈中取出。不如直接使用「数组模拟栈」结构:通过在数组尾部进行「尾插尾删」操作,模拟栈的「进栈」和「出栈」。最终,数组中剩下的内容就是消除后的结果。

【代码实现】

cpp 复制代码
class Solution {
public:
    string removeDuplicates(string s) 
    {
        string ret;
        for(auto ch : s)
        {
            if(ret.size() == 0 || ret.back() != ch) ret += ch;//入栈
            else ret.pop_back(); //出栈
        }
        return ret;
    }
};

844. 比较含退格的字符串

【题目 】:844. 比较含退格的字符串

【算法思路】

该问题可以分为两个部分:判断两个字符串在删除'退格字符'后的结果是否相等,以及如何获取去除'退格字符'后的字符串。由于涉及到不断的插入和删除操作,使用栈的结构最为合适。可以设计一个函数来处理'退格字符'的逻辑,函数内部通过栈操作来实现。需要注意的是,当栈为空时,不能执行pop操作。

【代码实现】

cpp 复制代码
class Solution {
public:
    bool backspaceCompare(string s, string t) 
    {
        return check(s) == check(t);
    }

    string check(string& str)
    {
        string ret;
        for(auto ch : str)
        {
            if(ch != '#')
            {
                ret += ch;
            }
            else
            {
                if(ret.size()) ret.pop_back();
            }
        }
        return ret;
    }
};

227. 基本计算器 II

【题目 】:227. 基本计算器 II

【算法思路】

解法:利用栈模拟计算过程

对于表达式求值问题,通常使用栈来模拟计算过程,并结合分情况讨论。在这里,可以通过一个栈来优化原本需要使用双栈的做法。为简化累加过程,我们可以为每个数字添加符号,并在遍历过程中处理*和/操作。

输入字符串中可能包含空格,需要特别处理。根据表达式的形式数字1 操作符 数字2,当遇到数字2时,可以决定是将其入栈,还是与栈顶元素进行乘除运算。类似地,数字1实际上是之前的数字2,所有这些操作通过一套统一的逻辑来完成。

对于多位数字,可以使用如下代码来处理:
while (i < n && s[i] >= '0' && s[i] <= '9') tmp = tmp * 10 + (s[i++] - '0');

【代码实现】

cpp 复制代码
class Solution {
public:
    int calculate(string s) 
    {
        vector<int> stack;
        char op = '+';
        int i = 0, n = s.size();
        while(i < n)
        {
            //处理为空的情况
            if(s[i] == ' ') i++;
            //遇到数字
            else if(s[i] >= '0' && s[i] <= '9')
            {
                int tmp = 0;
                while( i < n && s[i] >= '0' && s[i] <= '9') tmp = tmp * 10 + (s[i++] - '0');

                //单或多个字符
                if(op == '+') stack.push_back(tmp);
                else if(op == '-') stack.push_back(-tmp);
                else if(op == '*') stack.back() *= tmp;
                else stack.back() /= tmp;
            }
            //遇到字符
            else
            {
                op = s[i++];
            }
        }
        //处理剩下求和
        int sum = 0;
        for(auto x : stack) sum += x;
        return sum;
    }
};

394. 字符串解码

【题目 】:394. 字符串解码

【算法思路】

解法:用栈模拟

这类问题通常可以通过模拟过程来解决,尤其是遇到类似表达式求值的问题时,通常需要借助栈,并结合分情况讨论。

【代码实现】

cpp 复制代码
class Solution {
public:
    string decodeString(string s) 
    {
        stack<string> st;
        stack<int> nums;
        st.push("");
        int i = 0, n = s.size();

        while(i < n)
        {
            //遇到数字
            if(s[i] >= '0' && s[i] <= '9')
            {
                int tmp = 0;
                while(s[i] >= '0' && s[i] <= '9') 
                    tmp = tmp * 10 + (s[i++] - '0');

                nums.push(tmp);
            }
            else if(s[i] == '[')
            {
                i++;
                string tmp = "" ;
                while(s[i] >= 'a' && s[i] <= 'z') tmp += s[i++];

                st.push(tmp);

            }
            else if(s[i] == ']')
            {
                string tmp = st.top();
                st.pop();
                //解析
                int k = nums.top();
                nums.pop();
                
                while(k--)
                {
                    st.top() += tmp; 
                }
                i++;

            }
            else//遇到单独情况,直接放在字符串栈顶字符串后面
            {
                string tmp;
                while(i < n && s[i] >= 'a' && s[i] <= 'z')  tmp+= s[i++];
                st.top() += tmp;
            }
        }
        return st.top();
    }
};

946. 验证栈序列

【题目 】:946. 验证栈序列

【算法思路】

通过栈模拟进出栈的过程。不断将元素进栈,并在每次进栈时判断是否需要出栈。若所有元素处理完后栈中仍有元素,则说明序列无效;否则,序列是合法的。

【代码实现】

cpp 复制代码
class Solution {
public:
    bool validateStackSequences(vector<int>& pushed, vector<int>& popped) 
    {
        vector<int> stack;
        int j = 0;
        for(int i = 0; i <pushed.size(); i++)
        {
            stack.push_back(pushed[i]);
            while(j < popped.size() && !stack.empty() && popped[j] == stack.back())
            {
                stack.pop_back();
                j++;
            }
        }
        return stack.empty();
    }
};


快和小二一起踏上精彩的算法之旅!关注我,我们将一起破解算法奥秘,探索更多实用且有趣的知识,开启属于你的编程冒险!

相关推荐
akinggw21 分钟前
libagentcore - 一个基于c++的AI多agent系统升级到2.0版本
开发语言·jvm·c++
繁星蓝雨1 小时前
C++设计原理———多重继承(虚基类、菱形继承、委托、重命名、基类和成员初始化)
c++·委托·多重继承·mi·显式初始化列·虚基类·对象布局
旖旎夜光2 小时前
【C++ 基础入门】命名空间、输入输出、缺省参数与函数重载
开发语言·c++·笔记·学习
扶尔魔ocy3 小时前
【QT入门到晋级】用开源的自定义控件开发
c++·qt·自定义控件
AiNightVision3 小时前
无人机夜视模块如何选择?——从参数指标到系统集成的完整选型指南
人工智能·算法·计算机视觉·车载系统·无人机·视频·智能硬件
牧羊人.3334 小时前
卷积神经网络之VGG
人工智能·深度学习·神经网络·算法·cnn
akinggw4 小时前
libsherpacore - 基于 sherpa-onnx 的 C++ 语音交互静态库
c++·交互·语音识别
xiaoye-duck4 小时前
《C++ 项目:手写大模型接入 SDK》ChatGPT接口设计(中):全量响应实现和测试
网络·c++·语言模型·chatgpt
小郎君6584 小时前
Vue 多标签页的加载态设计:为什么我没有用 Suspense 实现 PageLoading
vue.js·算法
JJJennie7775 小时前
MAI Gateway私有化部署全攻略:大模型网关如何接入?附架构图
算法·gateway·ai网关·魔芋ai