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

文章目录

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

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链接

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

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

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

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

示例 1:

输入:s = "3a2bc"

输出:"aaabcbc"

示例 2:

输入:s = "3a2\[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;
    }
}

总结

  • 入栈和出栈序列匹配问题
相关推荐
江畔柳前堤3 小时前
大语言模型分布式训练:从并行策略到万卡工程的系统梳理
人工智能·分布式·深度学习·算法·目标检测·机器学习·语言模型
Forever Nore4 小时前
LeetCode 4 寻找两个正序数组的中位数 - 二分
算法·leetcode
罗西的思考6 小时前
【OpenClaw具身硬件】MiniClaw 阅读笔记---(1)基础
人工智能·算法·机器学习
蛋先生DX6 小时前
大模型参数存储格式揭秘:BF不是男朋友
深度学习·算法·llm
爱跳舞的烤冷面7 小时前
自学嵌入式第22天(数据结构——哈希)
数据结构·算法·哈希算法
猎嘤一号7 小时前
博弈论(Game Theory)的理论、算法与工程
人工智能·算法·安全·博弈论
月光船幽幽7 小时前
影子模式下保护 logits 不被修改
人工智能·python·算法
马拉AI7 小时前
腾讯开源 Agent 记忆系统,AI“换对话就忘”的问题有了新解法(附安装使用教程)
人工智能·算法·开源·科研
地平线开发者9 小时前
征程6工具链模型X86推理方式说明
算法
地平线开发者9 小时前
【征程6】校准量化中HistogramObserver解析
算法