【优选算法】专题十二——栈

文章目录

一、删除字符串中的所有相邻重复项

Leetcode链接

给出由小写字母组成的字符串 s,重复项删除操作会选择两个相邻且相同的字母,并删除它们。

在 s 上反复执行重复项删除操作,直到无法继续删除。

在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。

解题思路

  • 读到这种题就要意识到与栈这种数据结构特性的相似性
  • 将每个字符依次入栈,每次与栈顶元素进行比较,相同的话就要删掉栈顶元素,接着往后遍历。我们可以使用数组、或者在本题中可以使用StringBuilder来模拟栈。

代码实现及解析

java 复制代码
class Solution {
    public String removeDuplicates(String s) {
        StringBuilder str=new StringBuilder();//模拟一个栈
        for(char ch:s.toCharArray()){
            if(str.length()>0&&ch==str.charAt(str.length()-1)){//如果当前遍历元素与str末尾(栈顶)元素相等,就删掉栈顶元素
                str.deleteCharAt(str.length()-1);
            }else{
                str.append(ch);
            }
        }
        return str.toString();

    }
}

总结

  • 栈的性质的应用,复习解题思路

二、基本计算器 II

Leetcode链接

给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。

整数除法仅保留整数部分。

你可以假设给定的表达式总是有效的。所有中间结果将在 [-231, 231 - 1] 的范围内。

注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval() 。

提示:

1 <= s.length <= 3 * 105

s 由整数和算符 ('+', '-', '*', '/') 组成,中间由一些空格隔开

s 表示一个 有效表达式

表达式中的所有整数都是非负整数,且在范围 [0, 231 - 1] 内

题目数据保证答案是一个 32-bit 整数

示例 :

输入:s = "3+2*2"

输出:7

解题思路

  • 这类"表达式求值"题目都是使用栈来解答

  • 对于'+'、'---'后面的数字我们不能直接运算,因为不知道它们后面的数字是否还连着'*'、'/' 运算符,所以我们应该先处理乘除运算,最后再计算加减。我们先定义一个变量op记录最新更新的运算符是什么,判断遍历到的数字num的前面的运算符(也就是最新更新的运算符op)是什么来处理该数字并将其入栈,op是'+'就直接将其入栈,是'---'就取相反数入栈,是'*'、'/'运算符就取栈顶元素与num运算并将结果入栈。

  • 最后只需将栈中元素全部加和就可得出正确结果

  • 没有括号,所以我们不需要处理括号问题,并且每一个数都是大于等于0的

代码实现及解析

java 复制代码
class Solution {
    public int calculate(String s) {
        char[] str=s.toCharArray();
        Stack<Integer> stack=new Stack<>();//最终储存在Stack中的就是在处理完乘、除法之后的所有数
        char op='+';//op单独记录最近一次更新的运算符(初始化为'+')
        int i=0;
        while(i<str.length){
            if(str[i]==' '){//千万不要忘记处理空格字符
                    i++;
                }else if(str[i]>='0'&&str[i]<='9'){//数字
                int num=0;
                while(i<str.length&&str[i]>='0'&&str[i]<='9'){//将连续的数学字符处理为完整的数学自然数
                    num*=10;
                    num+=str[i]-'0';
                    i++;
                }
                //开始判断上一次最新更新的op的类型,并根据op对num进行处理
                if(op=='+'){
                    stack.push(num);
                }else if(op=='-'){
                    stack.push(-num);//这样最终可以对栈中元素直接进行加和
                }else if(op=='*'){
                    stack.push(stack.pop()*num);
                }else{
                    stack.push(stack.pop()/num);
                }
            }else{
                op=str[i++];//更新操作符
            }
        }
        //最后就可以把栈中的数全部加和
        int sum=0;
        while(!stack.empty()){
            sum+=stack.pop();
        }
        return sum;
    }
}

总结

  • 复习解题思路

三、字符串解码

Leetcode链接

给定一个经过编码的字符串,返回它解码后的字符串。

编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。

你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。

此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。

示例 1:

输入:s = "3[a]2[bc]"

输出:"aaabcbc"

示例 2:

输入:s = "3[a2[c]]"

输出:"accaccacc"

代码实现解析

java 复制代码
class Solution {
    public String decodeString(String s) {
        char[] str=s.toCharArray();
        Stack<StringBuilder> stringStack=new Stack<>();
        stringStack.push(new StringBuilder());//提前入栈一个空字符串,否则第一次解析的字符串没地方拼接
        Stack<Integer> numsStack=new Stack<>();
        int i=0;
        while(i<str.length){//遍历字符串
            if(str[i]>='0'&&str[i]<='9'){//是数字
                int tmpNum=0;
                while(str[i]>='0'&&str[i]<='9'){//把数字提取出来
                    tmpNum*=10;
                    tmpNum+=str[i++]-'0';
                }//i到'['位置,循环结束
                numsStack.push(tmpNum);//将数字入栈
                //下面开始把'['后面的字符串提取出来
                i++;
                StringBuilder tmpStr=new StringBuilder();
                while(str[i]>='a'&&str[i]<='z'){
                    tmpStr.append(str[i++]);
                }
                stringStack.push(tmpStr);//将暂时未解析的字符串先单独入栈,下次需要处理直接pop()
                //循环结束后还不知道此时i位置是什么字符,可能是']',也可能嵌套了编码字符串,那i位置就是会是数字字符
            }else if(str[i]==']'){
                //遇到了']',开始解析
                int k=numsStack.pop();
                StringBuilder tmp=stringStack.pop();
                for(int j=0;j<k;j++){//用j别与上面的i冲突了
                    stringStack.peek().append(tmp);//将解析后的的字符串直接追加到栈顶字符串后面(与未解析的处理方式就不一样)
                }
                i++;//往后遍历
            }else{//若单独遇见字符(串)(也就是不在'[]'里面)
                while(i<str.length&&str[i]>='a'&&str[i]<='z'){//这个操作可就要注意i可能会越界了
                    stringStack.peek().append(str[i++]);//这样的直接在处理过的字符串(栈顶)后面追加
                }
            }

        }
        return stringStack.pop().toString();
    }
}

总结

  • 解题思路复习代码自己再琢磨一遍
  • 要找到由括号界定的数据处理规则、分情况讨论,比如本题:
  • 未解析的字符串要单独入栈,下次需要解析了直接pop。而解析过的字符串要追加( append() )到字符串栈的栈顶字符串的后面,因为解析过的本来就要追加到结果的后面,而假如整体还需要被解析,那么也正好是要追加到后面再解析的。

四、验证栈序列

Leetcode链接

给定 pushed 和 popped 两个序列,每个序列中的 值都不重复,只有当它们可能是在最初空栈上进行的推入 push 和弹出 pop 操作序列的结果时,返回 true;否则,返回 false 。

解题思路

  • 这种选择题考的也多,其实就是把做选择题的过程模拟一遍。
  • 再把pushed数组入栈的过程中与poped数组对比,看是否可以出栈,最后如果正常匹配的话栈中的元素应该都被pop()出去了,栈为空。

代码实现及解析

java 复制代码
class Solution {
    public boolean validateStackSequences(int[] pushed, int[] popped) {
        Stack<Integer> stack=new Stack<>();
        int j=0;
        for(int i=0;i<pushed.length;i++){
            stack.push(pushed[i]);//入栈
            while(!stack.empty()&&stack.peek()==popped[j]){//入栈后就要检查是否可以出栈
                stack.pop();
                j++;
            }
        }
        if(stack.empty()) return true;//正常情况如果匹配的话stack中就不应该还留有元素
        else return false;
    }
}

总结

  • 入栈和出栈序列匹配问题
相关推荐
无心水2 小时前
【任务调度:框架】10、2026最新!分布式任务调度选型决策树:再也不纠结选哪个
人工智能·分布式·算法·决策树·机器学习·架构·2025博客之星
我头发还没掉光~2 小时前
【C++写详细总结①】从for循环到算法初步
数据结构·c++·算法
【数据删除】3482 小时前
计算机复试学习笔记 Day41
笔记·学习·算法
上海锟联科技2 小时前
什么是DAS分布式光纤声波传感系统?原理与应用解析
数据结构·分布式·算法·分布式光纤传感
篮l球场2 小时前
LRU 缓存
算法·leetcode
blackicexs3 小时前
第八周第五天
数据结构·c++·算法
whycthe3 小时前
c++二叉树详解
数据结构·c++·算法
郝学胜-神的一滴3 小时前
循环队列深度剖析:从算法原理到C++实现全解析
开发语言·数据结构·c++·算法·leetcode
Via_Neo3 小时前
接雨水问题 + 输入优化
java·开发语言·算法