Day10
20. 有效的括号
力扣题目链接
给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
思路
这种处理对称性的问题,可以使用栈来解决
不符合题意有三种情况:左括号多,右括号多,左右括号不匹配
每次加入的元素有几种情况:
如果是左括号,就直接加入对应的右括号即可
如果是右括号,这时候按道理应该从栈中pop元素,但如果这时候栈空(右括号多),直接返回false
栈不空,就比较栈peek的元素和待比较的右括号是否相等,不等直接返回false
相等,就pop元素,进行下一次循环
循环结束之后,由于可能左括号多,也就是这时候栈非空,也返回false
时间复杂度O(n) 遍历一遍字符串
空间复杂度O(n) 使用栈的辅助空间
代码
class Solution {
public boolean isValid(String s) {
Deque<Character> stack = new ArrayDeque();
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '(') {
stack.push(')');
continue;
}
if (s.charAt(i) == '[') {
stack.push(']');
continue;
}
if (s.charAt(i) == '{') {
stack.push('}');
continue;
}
if (stack.isEmpty() || stack.pop() != s.charAt(i)) {//防止右括号多和左右括号不匹配
return false;
}
}
return stack.isEmpty();//防止左括号多
}
}
注意:char类型不是对象,直接写入泛型的数据类型中是错误的,需要使用它的包装类Character。
题外话:
为什么一般都使用 List list = new ArrayList() ,而不用 ArrayList alist = new ArrayList()呢?
问题就在于List有多个实现类,如 LinkedList或者Vector等等,现在你用的是ArrayList,也许哪一天你需要换成其它的实现类呢?
这时你只要改变这一行就行了:List list = new LinkedList(); 其它使用了list地方的代码根本不需要改动。
假设你开始用 ArrayList alist = new ArrayList(), 这下你有的改了,特别是如果你使用了 ArrayList特有的方法和属性。 ,如果没有特别需求的话,最好使用List list = new LinkedList(); ,便于程序代码的重构. 这就是面向接口编程的好处
list只能使用ArrayList中已经实现了的List接口中的方法,ArrayList中那些自己的、没有在List接口定义的方法是不可以被访问到的
list.add()其实是List接口的方法,但是调用ArrayList的方法如 clone()方法是调用不到的
1047. 删除字符串中的所有相邻重复项
力扣题目链接
给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
思路
使用栈很快
对每个元素,如果栈非空且与栈顶元素匹配,那么将栈顶元素直接弹出
否则,可能栈空或者不匹配,就把这个元素入栈
遍历结束之后,栈内的元素就是剩下的,把它们依次pop出
注意,如果用StringBuilder接收,需要再翻转一下
代码
class Solution {
public String removeDuplicates(String s) {
Deque<Character> stack = new ArrayDeque<>();
for (int i = 0; i < s.length(); i++) {
if (!stack.isEmpty() && stack.peek() == s.charAt(i))
stack.pop();
else
stack.push(s.charAt(i));
}
StringBuilder builder = new StringBuilder();
while (!stack.isEmpty())
builder.append(stack.pop());
return new String(builder.reverse());
}
}
150. 逆波兰表达式求值
力扣题目链接
根据 逆波兰表示法,求表达式的值。
思路
逆波兰表达式求值是典型的使用栈处理的问题
遍历数组,遇到数字直接进栈
遇到符号,把栈顶的两个元素运算(后pop出的对先pop出的运算),之后再入栈
最后栈内只有一个元素,出栈即可
代码
class Solution {
public int evalRPN(String[] tokens) {
Deque<Integer> stack = new ArrayDeque<>();
for (int i = 0; i < tokens.length; i++) {
String cur = tokens[i];
if ("+".equals(cur)) {
stack.push(stack.pop() + stack.pop());
} else if ("-".equals(cur)) {
int temp = stack.pop();
stack.push(stack.pop() - temp);
} else if ("*".equals(cur)) {
stack.push(stack.pop() * stack.pop());
} else if ("/".equals(cur)) {
int temp = stack.pop();
stack.push(stack.pop() / temp);//注意减法和除法的特殊处理
} else {
stack.push(Integer.valueOf(cur));
}
}
return stack.pop();
}
}
总结
今天的几个题目都是栈的应用,当看到对称问题的时候可以使用栈处理
有效的括号:如果是左括号,加入对应的右括号,这样比较起来方便,返回false有三种情况:要么不匹配,要么左括号多,要么右括号多,分别进行考虑
删除相邻重复项:还是栈的原理,保证栈非空前提下,每次看看栈顶元素和要加入的是否一样,一样就pop出,不一样或者空就加入这个元素
逆波兰表达式求值:遍历字符串数组,如果是数字直接入栈,如果是符号,要从栈中pop两个元素,后pop出的对先pop出的做运算,将结果再入栈即可
加油!