算法之栈(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. 字符, 这时说明遇到了一个"裸"的字符串, 即只单纯重复一次, 同上面一样, 与栈顶字符串合并.

以一个复杂的例子为例: 3[z] 2[2[y]pq4[a]] 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();
    }
};

暂完

相关推荐
爱编程— 的小李7 分钟前
有序序列合并(c语言)
c语言·算法
云卓SKYDROID9 分钟前
无人机反步滑膜控制算法!
算法·无人机·知识科普·云卓科技·反步滑膜控制算法
云卓科技9 分钟前
无人机之自动控制原理篇
科技·算法·目标检测·机器人·无人机
云卓科技11 分钟前
无人机之集群控制方法篇
科技·算法·无人机·交互·制造
混迹网络的权某12 分钟前
每天一道C语言精选编程题之求数字的每⼀位之和
c语言·开发语言·考研·算法·改行学it·1024程序员节
Curry_Math15 分钟前
LeetCode 热题 100之链表3
算法·leetcode·链表
hn小菜鸡3 小时前
LeetCode 2058.找出临界点之间的最小和最大距离
算法·leetcode·职场和发展
liuyang-neu3 小时前
力扣 简单 70.爬楼梯
java·算法·leetcode
IronmanJay3 小时前
【LeetCode每日一题】——862.和至少为 K 的最短子数组
数据结构·算法·leetcode·前缀和·双端队列·1024程序员节·和至少为 k 的最短子数组
OT.Ter3 小时前
【力扣打卡系列】二分查找(搜索旋转排序数组)
算法·leetcode·职场和发展·go·二分查找