算法之栈(stack)

栈本身不是一个很难的数据结构, 但是对应的算法题有一定难度, 难在我们是否能想到这道题用栈解决. 如果知道用栈解决这道题, 那其实本质就是模拟.

题目1: 删除字符中的所有相邻重复项

cpp 复制代码
class Solution {
public:
    string removeDuplicates(string s) {
        stack<char> st;
        string ret;
        
        for(const auto& e: s)
        {
            if(st.empty())
                st.push(e);
            else if(e == st.top())
                st.pop();
            else
                st.push(e);
        }
        
        while(!st.empty())
        {
            ret += st.top();
            st.pop();
        }
        reverse(ret.begin(),ret.end());
        return ret;
    }
};

用栈最后把元素拷贝到字符串中, 最后还需要逆序一次, 可以直接用字符串模拟栈的行为即可:

cpp 复制代码
class Solution {
public:
    string removeDuplicates(string s) {
        string ret;
        for(const auto& e: s)
        {
            if(ret.length() == 0)
                ret += e;
            else if(ret.back() == e)
                ret.pop_back();
            else
                ret += e;
        }
        return ret;
    }
};

题目1.2 比较含退格的字符串

这题和题目1几乎一样, 用栈模拟即可.

cpp 复制代码
class Solution {
public:
    bool backspaceCompare(string s, string t) {
        string tmpS, tmpT;
        for(const auto& e : s)
        {
            if(!tmpS.empty() && e == '#')
            //e为#,栈不为空,就退格
                tmpS.pop_back();
            else if(e == '#')
            //如果e为#, tmpS为空, 就不用退格了
                continue;
            else
                tmpS += e;
        }

        //对T进行同样的操作
        for(const auto& e : t)
        {
            if(!tmpT.empty() && e == '#')
                tmpT.pop_back();
            else if(e == '#')
                continue;
            else
                tmpT += e;
        }

        return tmpS == tmpT;
    }
};

题目2:基本计算器 II

此题有两种思路, 比较麻烦的一种是中缀转后缀表达式, 然后后缀表达式进行计算, 基本计算器I 可以用这种思路解.

由于此题没有括号的运算, 只有加减乘除, 可以直接从左到右依次遍历, 将值 或者 **通过乘除法计算后的值 ,**放入栈中, 然后依次出栈将结果累加即可, 具体分为:

  1. 符号是+/-, 该数是否被立即计算是不确定的, 先入栈等待, 如果是减则入栈一个负值, 因为最后的计算全是加法. 如果下一个符号仍是+-, 则前一个数就被保留至最终运算了, 比如2+3

  2. 符号是*/, 这是此题优先级最高的运算, 遇到它需要把栈顶正在"等待"的元素取出进行乘除法运算, 再放回栈里. 比如 2+ 3 * 4, 遇到4时取出栈顶的3, 计算为12压入栈中.

cpp 复制代码
class Solution {
public:
   
    int calculate(string s) {
        stack<int> st;
        char Op = '+';
        int left = 0, right = 0;
        int n = s.size();
        while(right < n)
        {
            //跳过空白
            if(s[right] == ' ')
                ++right;
            //数字
            else if(isdigit(s[right]))
            {
                //先把数字提取出来, 可能有多位
                int num = 0;
                while(right < n && isdigit(s[right]))
                    num = num*10 + (s[right++]-'0');
                if(Op == '+') st.push(num);
                else if(Op == '-') st.push(-num);
                else if(Op == '*')
                {
                    num = st.top() * num;
                    st.pop();
                    st.push(num);
                }
                else 
                {
                    num = st.top() / num;
                    st.pop();
                    st.push(num);
                }
            }
            else
                Op = s[right++];
        }

        int ret = 0;
        while(!st.empty())
        {
            ret += st.top();
            st.pop();
        }
        return ret;
    }
};

题目3: 字符串解码

此题思路要用到双栈, 由于我们需要从内向外解码字符串, 每次都要先解码内部\[\]的字符串, 层层向外.

从左到右扫描, 将扫描的内容分为四种情况:

  1. 数字num, 则压入数字栈中, 等待 ']', 作为字符串重复的次数

  2. '', 将后面紧跟的(也可能没有)字符串压入**字符串栈**中, 等待'', 作为字符串重复的内容

  3. ']', 取出两个栈栈顶的元素, 开始重复. 将结果与**新栈顶的字符串合并,**因为内层解码的目的是把内容提供给外层作为外层字符串的一部分, 所以需要合并.

  4. 字符, 这时说明遇到了一个"裸"的字符串, 即只单纯重复一次, 同上面一样, 与栈顶字符串合并.

以一个复杂的例子为例: 3z 22\[ypq4a] ef

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

        while (i < n) 
        {
            if (isdigit(s[i])) 
            {
                int tmpNum = 0;
                while (i < n && s[i] >= '0' && s[i] <= '9')
                    tmpNum = tmpNum * 10 + (s[i++] - '0');
                stNum.push(tmpNum);
            } 
            else if (s[i] == '[') 
            {
                ++i;
                string tmpStr;
                while (isalpha(s[i]))
                    tmpStr += s[i++];
                
                stStr.push(tmpStr);
            } 
            else if (s[i] == ']') 
            {
                int num = stNum.top();
                stNum.pop();

                string str = stStr.top();
                stStr.pop();

                while (num--)
                    stStr.top() += str;
                ++i;
            }
            else 
            {
                while (i < n && isalpha(s[i]))
                    stStr.top() += s[i++];
            }
        }

        return stStr.top();
    }
};

题目4: 验证栈序列

此题和数据结构做过的选择题很像, 不过验证是否能按照指定顺序出栈 .

思路都是一样的, 依次将pushed的元素入栈, 每入一个元素就检查一次poped栈是否能出栈, 而且要循环检查, 因为可能出了一个元素就能出一连串的元素. 最后所有元素都出栈则验证成功.

cpp 复制代码
class Solution {
public:
    bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
        stack<int> st;
        int j = 0;
        for(const auto & e : pushed)
        {
            st.push(e);
            while(!st.empty() && st.top() == popped[j])
            {
                st.pop();
                ++j;
            }
        }
        return st.empty();
    }
};

暂完

相关推荐
zyl7658 小时前
FlashAttention 详解:从 GPU 硬件、普通 Attention 到分块计算与 Online Softmax
算法
爱读书的小胖12 小时前
LeetCode 122:买卖股票的最佳时机 II(C)解题分析
算法
结局无敌12 小时前
React Native for OpenHarmony 实战:三方库 react-native-modal-dialog 的鸿蒙化适配指南
深度学习·算法·react native·react.js·harmonyos
OPEN-F12 小时前
ROS2系列教程:上位机建图与导航(原理综合)
算法·机器人
weixin_3077791312 小时前
C++代码实现MATLAB中的fitrtree函数功能
开发语言·c++·算法·matlab
tomwang032213 小时前
路径优化建模、优化算法分类及工程化小结
算法·数学建模·分类
小羊没烦恼!13 小时前
Ajax与JSON的一些总结
android·java·前端·数据库·mysql·算法·okhttp
2501_9269783313 小时前
人机通信协议--人和机器之间的交互想象
人工智能·经验分享·算法·ai写作
行者全栈架构师13 小时前
【鸿蒙心迹】互动卡片全链路——把核心功能放到桌面(FormKit 实战)(HarmonyOS 7.x)
前端·算法·架构
无定义_13 小时前
10.11日常训练
算法