Day11
前言
临时决定这周末回家,希望不影响今日的进度
文章:代码随想录
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,主要还进一步发现并学习了栈与队列的定义问题
栈与队列的思想比我想的要容易,几道题的思路大同小异