代码随想录Day11 | 20. 有效的括号,1047. 删除字符串中的所有相邻重复项,150. 逆波兰表达式求值

Day11

前言

临时决定这周末回家,希望不影响今日的进度

文章:代码随想录

视频:算法公开课-跟着Carl学算法

LeetCode 20 有效的括号

自己思路

思考了一下我认为不匹配的场景,分为三类:一类是单一括号但是不成对,一类是小的括号夹了大的括号,一类是大的括号夹了小的括号但是小的括号不成对。感觉思绪还是很乱,没发现这和栈有什么关系,去看视频

看完讲解

总体思路很巧妙,利用栈对称匹配,然后我前面的分析有问题,因为其实这个题目不用考虑括号的层级问题,只看匹不匹配即可。那么不匹配的情况还是分为三类:一类是多了左括号,一类是多了右括号,一类是左右括号不匹配,卡哥的这个分析真的很牛。具体操作就是,每当遇到了左括号,就把对应的右括号(方便后面比较)加入栈内(用栈就可以保证后进去的可以先进行匹配,符合对称逻辑),如果遇到了右括号,那么就与栈内的元素进行比较。那么对应前三类在这个过程中,就会分别出现:遍历完了栈还有元素(多左),还没遍历完栈已经空了(多右),遍历元素与栈顶元素不相等(左右不匹配)

至于具体的代码,就是对字符串进行遍历,每一次循环进行if判断,如果是左括号,就加入右括号进栈,如果遇到了右括号,就先判断是否为空栈以及元素是否相等(解决二三类),如果都不是那就说明该位匹配上了,pop出栈,进入下一次循环。如果循环结束之后,栈非空就也是false(解决一类),这样就可以完成。记得开头还可以先对字符串长度进行判断,因为如果长度不是偶数,那么一定是不匹配的括号

class Solution {
    public boolean isValid(String s) {
        if (s.length() % 2 != 0) {
            return false;
        }
        Stack<Character> stack = new Stack<>();
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == '{') {
                stack.push('}');
            } else if (s.charAt(i) == '[') {
                stack.push(']');
            } else if (s.charAt(i) == '(') {
                stack.push(')');
            } else if (stack.isEmpty() || s.charAt(i) != stack.peek()) {
                return false;
            } else {
                stack.pop();
            }
        }
        return stack.isEmpty();
    }
}

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

自己思路

感觉这道题有思路,有提示是用栈来解决。大体想法就是,字符串从左到右遍历一遍,每遇到一个元素,就去栈里看看,如果栈为空或者该元素与栈顶元素不相等,那么就将该元素入栈;如果与栈顶元素相等,那么就要将栈顶元素弹出。那么进行到最后,栈内留存的就是最后没有重复的元素,但是如果一个一个再弹出赋值给新字符串的话,会与答案相反,所以我想的方法是用另外一个栈来接收弹出的元素,然后再遍历赋值给字符串,就可以把顺序纠正。这个时候甚至感觉如果用队列,好像还可以规避这个相反的问题。但是奈何写出来的代码就是不对,会比正确答案缺少一个元素,也找不到问题,而且我的这个反了的处理真的很麻烦,看视频吧

看完讲解

感觉我的思路和卡哥非常一致,利用栈匹配相邻元素,关于我前面提到的会反的问题,我真的太傻了,忘记了字符串不仅可以向右拼接,也可以向左拼接呀,所以其实只要向左拼接就行,根本就不会反。卡哥还提到可以直接就用字符串来代替栈,也相当于向左拼接就可以,但是Java中字符串不可变,所以用字符串好拼接但是不能弹出,Java还是更适合用栈来处理

最后终于发现了为什么我前面会总比正确答案少一个元素,问题在于最后将栈内所有元素依次弹出并向左拼接字符串的时候,不能使用for循环,因为for循环的判断条件是stack.size()的话,每次循环pop出一个元素后,这个size会改变,除非提前用变量获取好,还是while更省事

去回看了一下为什么225的用队列实现栈当时用for循环没有问题,因为当时是对队列queue.size()进行判断,但是当时是会对队列先进行弹出再进行加入,最终queue.size()并不会改变,所以不会有影响

class Solution {
    public String removeDuplicates(String s) {
        Stack<Character> stack = new Stack<>();
        for (int i = 0; i < s.length(); i++) {
            if (stack.isEmpty() || s.charAt(i) != stack.peek()) {
                stack.push(s.charAt(i));
            }
            else {
                stack.pop();
            }
        }

        String result = "";
        while (!stack.isEmpty()) {
            result = stack.pop() + result;
        }  
        return result;
    }
}

LeetCode 150 逆波兰表达式求值

自己思路

听从建议,先看视频

看完讲解

其实看视频之前还是自己写了一下,因为看了题目的提示,思路感觉已经很明显了。大概就是遍历字符串数组,如果遇到了数字就加入栈,如果遇到了运算符,就弹出栈顶的两个元素并进行对应的运算,再把结果填入栈中,最后等遍历结束,栈中剩下的那一个元素就是所求的值。虽然思路比较清晰,但是实现过程还是遇到不少困难,总是报错最后没有完全写出来,去看了讲解

看视频的确是利用栈运算相邻元素,看了视频的代码和答案代码之后,大概知道如何修改我的代码。首先,栈的类型可以是int,但是这样把字符串数组的元素放入栈的时候需要先进行Integer.valueOf()来转换类型,这样的话在过程中取栈顶两元素进行运算的时候,也可以直接运算(比定义字符串类型的栈,然后等运算的时候不断转化类型简洁)。然后if判断的时候,我当时先写的如果是数字,并且是用-200到200之间来判断,因为考虑到先判断运算符取元素的话会遇到空栈问题,结果这里就有字符串的比较需要用equals来,并且是字符串类型和数字类型的不好比较,最后采用的第一个判断写如果不是这四种运算符就怎么样,总之写得很复杂,但是其实大可不必,因为如果不是运算符就不会进入这个语句,就更不存在空栈的问题,所以其实判断的顺序就依次四个运算符再else就可以。最后是进行运算的时候要注意,加法和乘法毕竟有交换率所以可以不考虑顺序,但是对于除法和减法要注意哪个在前哪个在后,画个图就很清晰,所以这两个运算就要先取元素保存下来再运算。最后的话,因为本身就是int类型的栈,所以直接返回栈顶元素就可以

class Solution {
    public int evalRPN(String[] tokens) {
        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < tokens.length; i++) {
            if (tokens[i].equals("+")) {
                stack.push(stack.pop() + stack.pop());
            } else if (tokens[i].equals("-")) {
                int a = stack.pop();
                int b = stack.pop();
                stack.push(b - a);
            } else if (tokens[i].equals("*")) {
                stack.push(stack.pop() * stack.pop());
            } else if (tokens[i].equals("/")){
                int a = stack.pop();
                int b = stack.pop();
                stack.push(b / a);
            } else {
                stack.push(Integer.valueOf(tokens[i]));
            }
        }
        return stack.peek();
    }
}

总结

用时:4h,主要还进一步发现并学习了栈与队列的定义问题

栈与队列的思想比我想的要容易,几道题的思路大同小异

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值