一、LC20. 有效的括号
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
用栈来做。遍历字符串中的元素,左括号入栈,右括号和栈中最上面的元素比较,匹配的话继续比较,不匹配就返回false。需要考虑以下几种情况:
1. (){}
2.(]
3.] 或者()]) 右括号出现的时候左括号还没入栈。
4. (() 遍历过程中括号都匹配,但遍历结束时栈中还有未匹配的左括号。只有遍历结束栈中没有元素才是符合条件的字符串。
class Solution {
public boolean isValid(String s) {
Deque<Character> stack = new ArrayDeque<>();
Map<Character, Character> map = new HashMap<>();
map.put('(',')');
map.put('[',']');
map.put('{','}');
for (int i = 0; i < s.length(); i++) {
if (map.containsKey(s.charAt(i))) {
stack.push(s.charAt(i));
} else {
if (stack.isEmpty() || map.get(stack.poll()) != s.charAt(i)) {
return false;
}
}
}
if (stack.isEmpty()) {
return true;
}
return false;
}
}
- 时间复杂度: O(n)
- 空间复杂度: O(n)
二、LC1047. 删除字符串中的所有相邻重复项
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
本题要删除相邻相同元素,相对于20. 有效的括号 (opens new window)来说其实也是匹配问题,20. 有效的括号 是匹配左右括号,本题是匹配相邻元素,最后都是做消除的操作。
本题也是用栈来解决的经典题目。
那么栈里应该放的是什么元素呢?
我们在删除相邻重复项的时候,其实就是要知道当前遍历的这个元素,我们在前一位是不是遍历过一样数值的元素,那么如何记录前面遍历过的元素呢?
所以就是用栈来存放,那么栈的目的,就是存放遍历过的元素,当遍历当前的这个元素的时候,去栈里看一下我们是不是遍历过相同数值的相邻元素。然后再去做对应的消除操作。
因为从栈里弹出的元素是倒序的,所以再对字符串进行反转一下,就得到了最终的结果。
方法一:
class Solution {
public String removeDuplicates(String s) {
Deque<Character> stack = new ArrayDeque<>();
for (int i = 0; i < s.length(); i++) {
if (!stack.isEmpty() && (s.charAt(i) == stack.peek())) {
//stack不为空且当前元素等于stack中最后一个元素
stack.poll();
} else {
//1. stack为空
//2,当前元素不等于stack中最后一个元素
stack.push(s.charAt(i));
}
}
String result = "";
while (!stack.isEmpty()) {
result = stack.poll() + result;
}
return result;
}
}
- 时间复杂度: O(n)
- 空间复杂度: O(n)
方法二:拿字符串直接作为栈,省去了栈还要转为字符串的操作。
class Solution {
public String removeDuplicates(String s) {
// 将 res 当做栈
// 也可以用 StringBuilder 来修改字符串,速度更快
// StringBuilder res = new StringBuilder();
StringBuffer res = new StringBuffer();
// top为 res 的长度
int top = -1;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
// 当 top > 0,即栈中有字符时,当前字符如果和栈中字符相等,弹出栈顶字符,同时 top--
if (top >= 0 && res.charAt(top) == c) {
res.deleteCharAt(top);
top--;
// 否则,将该字符 入栈,同时top++
} else {
res.append(c);
top++;
}
}
return res.toString();
}
}
- 时间复杂度: O(n)
- 空间复杂度: O(n)
三、LC150. 逆波兰表达式求值
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
视频讲解:栈的最后表演! | LeetCode:150. 逆波兰表达式求值_哔哩哔哩_bilibili
逆波兰表达式:是一种后缀表达式,所谓后缀就是指运算符写在后面。
平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 ) 。
该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * ) 。
逆波兰表达式主要有以下两个优点:
-
去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。
-
适合用栈操作运算:遇到数字则入栈;遇到运算符则取出栈顶两个数字进行计算,并将结果压入栈中
思路:遍历输入,遇到数字加入栈中,遇到运算符号将栈中最顶部的两个元素pop做运算,将结果放回栈中方便后续计算。
在java中,int是32位有符号数据类型,其变量需要32位内存
class Solution {
public int evalRPN(String[] tokens) {
Deque<Integer> stack = new ArrayDeque<>();
for (int i = 0; i < tokens.length; i++) {
if (tokens[i].equals("+")) {
stack.push(stack.poll() + stack.poll());
} else if (tokens[i].equals("-")) {
stack.push(-stack.poll() + stack.poll());
} else if (tokens[i].equals("*")) {
stack.push(stack.poll() * stack.poll());
} else if (tokens[i].equals("/")) {
int tmp = stack.poll();
stack.push(stack.poll()/tmp);
} else {
System.out.println(tokens[i]);
stack.push(Integer.parseInt(tokens[i]));
}
}
return stack.pop();
}
}
- 时间复杂度: O(n)
- 空间复杂度: O(n)