代码随想录训练营D11-栈与队列篇 p2 |20. 有效的括号、1047. 删除字符串中的所有相邻重复项、150. 逆波兰表达式求值
(一) 20. 有效的括号
栈的经典应用。
1. 思路
1.1 直观思路
遍历读入s,每读一个字符时,先判断与栈顶元素能否凑一对,能凑一对,当前元素也不用入栈了,并且栈顶也顺便出栈;否则,当前元素入栈。
先读入的一定是左括号,并且消除时一定是右括号进入时。
写一个isMatch方法,用来判断’(‘、’)’ 等是一对的
1.2 代码随想录思路
没有考虑写isMatch方法,而是在起先存左括号时,就直接存的右括号,这样再右括号进入时可以直接判断两个字符是否相等,若相等则表明他们是一对的。
如何确定括号的左右呢?将([{三个括号都写在if中,else就是右括号。
有三种情况
1.匹配的过程中,发现栈空了,连字符都没了,返回false
2.匹配的过程中,发现当前字符与栈顶的不相等,返回false
3.字符串遍历完了,栈不为空,返回false
2. 代码
2.1 自己代码
class Solution {
public boolean isValid(String s) {
char[] chars = s.toCharArray();
Stack<Character> stack = new Stack<>();
for(char c : chars){
if(!stack.isEmpty()){
Character peek = stack.peek();
if(isMatch(peek, c)){
stack.pop();
continue;
}
}
stack.push(c);
}
return stack.isEmpty();
}
private boolean isMatch(char first, char second){
return (first == '(' && second == ')')
|| (first == '[' && second == ']')
|| (first == '{' && second == '}');
}
}
2.2 随想录代码
使用Deque表示栈,消耗更小一些,因为Deque是线程不安全的;而Stack是线程安全的。
public boolean isValid1(String s) {
Deque<Character> deque = new ArrayDeque<>();
char[] chars = s.toCharArray();
for(char c : chars){
if(c == '('){
deque.push(')');
}else if (c == '['){
deque.push(']');
}else if(c == '{'){
deque.push('}');
} else if(deque.isEmpty() || deque.peek() != c){//右括号会到这里
//上述为第1、2种情况
return false;
}else{//栈不为空,而且还匹配上了,那就栈顶出栈
deque.pop();
}
}
//栈若不为空,则还有没匹配完的,也就是无效括号,false
return deque.isEmpty();
}
(二)1047. 删除字符串中的所有相邻重复项
栈的经典应用。
要知道栈为什么适合做这种类似于爱消除的操作,因为栈帮助我们记录了 遍历数组当前元素时候,前一个元素是什么。
1. 思路
与上一题代码随想录思路基本相同,只是这一题甚至不用做左括号到右括号的转换。
三种情况:
1.匹配时,栈中为空了或没匹配上,直接压栈。
2.匹配时,匹配上了,栈顶出栈
循环结束,出栈组成字符串并返回。
2. 代码
class Solution {
public String removeDuplicates(String s) {
Deque<Character> deque = new ArrayDeque<>();
char[] chars = s.toCharArray();
for (char c : chars){
if(deque.isEmpty() || deque.peek() != c){
deque.push(c);
}else {
//匹配上了
deque.pop();
}
}
char[] result = new char[deque.size()];
int i = 0;
while(!deque.isEmpty()){
result[i++] = deque.pop();
}
//reverse 数组
int j = result.length - 1;
i = 0;
while(i < j){
char temp = result[j];
result[j] = result[i];
result[i] = temp;
++i;
--j;
}
return new String(result);
}
}
(三) 150. 逆波兰表达式求值
本题不难,但第一次做的话,会很难想到,所以先看视频,了解思路再去做题
1. 思路
一些小小的前置知识:
二叉树的遍历。都是指根结点的顺序。
先序遍历:根左右
后序遍历:左右根
中序遍历:左根右。
中缀表达式(中序遍历):(1+2)*(3+4)
后缀表达式(后序遍历):12+34+*
中缀表达式:适合人类宝宝的表达式,并且离不开括号。
后缀表达式:适合计算机宝宝的表达式,不需要括号表示优先级。
2.思路:
后缀表达式,也是栈的经典应用。
创建一个栈。将String转为数组后,依次读入。
遇到数字就压栈,遇到’+‘、’-‘、’*’ 和 '/'运算符就弹出两个数字进行计算。
循环结束后,栈中应该有一个元素就是整个式子的运算结果。
3.细节
先入栈的(即后出栈的)在运算符的前面,在/或-时。
2. 代码
public int evalRPN(String[] tokens) {
Deque<Integer> stack = new ArrayDeque<>();//直接创建数字类型的栈
int first;
for(String str : tokens){
if("+".equals(str)){
stack.push(stack.pop() + stack.pop());
}else if("-".equals(str)){
first = stack.pop();
stack.push(stack.pop() - first);
}else if("*".equals(str)){
stack.push(stack.pop() * stack.pop());
}else if("/".equals(str)){
first = stack.pop();
stack.push(stack.pop() / first);
}else {
//数字才会到这里,数字就压栈
stack.push(Integer.valueOf(str));
}
}
return stack.pop();
}