20有效的括号
给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。
- 思路:栈结构非常适合做匹配类的题目,首先就是先要判断什么时候是不匹配的情况。1是括号多了,二就是括号没匹配上。自己测试了一下这种情况就是“(
[ ) ] ”,这种返回的也是false,也就是说不允许这种交错嵌套,和代码语法的规则类似。用栈解决,自己没想出来,但是很巧妙。 - 一个一个的遍历括号,正常思路应该是,遍历一个存栈里一个,遇到反括号就对比之后是对应的反括号再出栈,不是对应的反括号就直接匹配失败,也就是字符串无效。
- 有个小巧思就是入栈的时候直接存上左括号对应的右括号,这样匹配出栈的时候 ,只需要匹配是否相等即可。字符串有效的条件就是,最后栈中的每一个左括号都和右括号恰好的匹配,也就是栈中是空的,并且不在空栈上进一步出栈。
代码:
class Solution {
public boolean isValid(String s) {
Stack a = new Stack();
for(int i=0;i<s.length();i++){//length后面没加括号
if(s.charAt(i)=='('){
a.push(')');
}else if(s.charAt(i)=='['){
a.push(']');
}else if(s.charAt(i)=='{'){
a.push('}');
}else{
if(!a.isEmpty()&&s.charAt(i)==')'&&a.peek().equals(')')){
//直接用==判断出错了,用了equals就好了
//开始没有判断栈是否为空
a.pop();
}else if(!a.isEmpty()&&s.charAt(i)==']'&&a.peek().equals(']')){
a.pop();
}else if(!a.isEmpty()&&s.charAt(i)=='}'&&a.peek().equals('}')){
a.pop();
}else{
return false;
}
}
}
if(a.isEmpty()){
return true;
}else{
return false;
}
}
}
代码随想录判断的比较简洁巧妙,时间也更快,但是不知道这个Deque是干嘛的,作用是什么,(是栈的一种吗?泛型里面是character也不懂)
class Solution {
public boolean isValid(String s) {
Deque<Character> deque = new LinkedList<>();
char ch;
for (int i = 0; i < s.length(); i++) {
ch = s.charAt(i);
//碰到左括号,就把相应的右括号入栈
if (ch == '(') {
deque.push(')');
}else if (ch == '{') {
deque.push('}');
}else if (ch == '[') {
deque.push(']');
} else if (deque.isEmpty() || deque.peek() != ch) {
return false;
}else {//如果是右括号判断是否和栈顶元素匹配
deque.pop();
}
}
//最后判断栈中元素是否匹配
return deque.isEmpty();
}
}
查了,deque是一个双端队列,支持在两端插入和移除元素,可以实现队列,也可以实现栈,deque介绍
Deque有三种用途:
普通队列(一端进另一端出):
Queue queue = new LinkedList()或Deque deque = new LinkedList()
双端队列(两端都可进出)
Deque deque = new LinkedList()
堆栈
Deque deque = new LinkedList()
character是char的引用类型,就是这个队列里只存放char类的元素。
1047. 删除字符串中的所有相邻重复项
给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
- 自己思路:做了刚刚的括号的题,感觉这个就有点思路了。还是利用栈的思想,首先先逐个存入栈中,如果要存入的下一个元素和当前的顶元素相同,那么不存入,并且把当前栈顶这个元素出栈,不同则入栈,最后栈中的元素就是最终的字符串
- 遇到的难点:怎么把栈中元素直接转成字符串,发现没有对应的方法,然后查了调研了几个,都不支持,最后用了题解的办法,弹出之后,倒序连接字符串,这样顺序就不会改变了
- 自己代码:
class Solution {
public String removeDuplicates(String s) {
Stack<Character> a = new Stack<>();
for(int i=0;i<s.length();i++){
if(a.isEmpty()){
a.push(s.charAt(i));
}else{
if(a.peek()==s.charAt(i)){
a.pop();
}else{
a.push(s.charAt(i));
}
}
}
//不知道用哪种方法把栈转成字符串:方法一不行
// StringWriter sw = new StringWriter();
// PrintWriter pw = new PrintWriter(sw);
// a.printStackTrace(pw);
//方法二也不行
//String sw = ExceptionUtils.getStackTrace(a);
//栈直接empty方法和isEmpty方法都能用呢,有啥不同呢?
//题解方法,妙啊,是在很妙a.pop()+tem 和 tem+a.pop()完全不一样
//直接实现倒序
String tem="";//注意这里不能是null
while (!a.empty()){
tem=a.pop()+tem;
}
return tem;
}
}
题解:
class Solution {
public String removeDuplicates(String S) {
//ArrayDeque会比LinkedList在除了删除元素这一点外会快一点
ArrayDeque<Character> deque = new ArrayDeque<>();
char ch;
for (int i = 0; i < S.length(); i++) {
ch = S.charAt(i);
if (deque.isEmpty() || deque.peek() != ch) {
deque.push(ch);
} else {
deque.pop();
}
}
String str = "";
//剩余的元素即为不重复的元素
while (!deque.isEmpty()) {
str = deque.pop() + str;
}
return str;
}
}
题解习惯用deque不知道为什么
应优先使用此接口而不是遗留 Stack 类。在将双端队列用作堆栈时,元素被推入双端队列的开头并从双端队列开头弹出
deque比stack
150. 逆波兰表达式求值
给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。
请你计算该表达式。返回一个表示表达式值的整数。
注意:
有效的算符为 ‘+’、‘-’、'’ 和 ‘/’ 。
每个操作数(运算对象)都可以是一个整数或者另一个表达式。
两个整数之间的除法总是 向零截断 。
表达式中不含除零运算。
输入是一个根据逆波兰表示法表示的算术表达式。
答案及所有中间计算结果可以用 32 位 整数表示。
示例 1:
输入:tokens = [“2”,“1”,“+”,“3”,""]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
逆波兰表达式:
逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。
平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 ) 。
该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * ) 。
逆波兰表达式主要有以下两个优点:
去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。
适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中
- 思路:其实看到这个题也是用栈来做,但是是因为他在这一专题,如果没在这专题的话可能不那么好想,且可以看成这个也是需要匹配的,有匹配的题就常常用栈。现在思路就很清晰,首先就是遍历这个数组,当遇到数字就入栈,当遇到符号就将栈顶的两个元素取出栈,用这个符号来运算,运算结果在存入栈中,依次重复这个过程。
- 自己代码,Idea通过了,但是力扣没通过,不知道为啥,而且有的idea没报错的语句,力扣都报错
class Solution {
public int evalRPN(String[] tokens) {
Deque<Integer> de = new LinkedList<>();
int re=0;
for(int i=0;i<tokens.length;i++){
if(tokens[i]=="+"&&!de.isEmpty()){
re = de.pop()+de.pop();
de.push(re);
}else if(tokens[i]=="-"&&!de.isEmpty()){
re=de.pop()-de.pop();
de.push(re);
}else if(tokens[i]=="*"&&!de.isEmpty()){
re=de.pop()*de.pop();
de.push(re);
}else if(tokens[i]=="/"&&!de.isEmpty()){
re=de.pop()/de.pop();
de.push(re);
}else{
de.push(Integer.parseInt(tokens[i]));
}
}
return re;
}
}
//开始是这个栈中存放那种数据类型才好,就迷茫了,直接int也报错,后来就变成String类型的,不知道为什么下面这个都是错的
// String a = de.pop();
// String b = de.pop();
// int re=de.pop()+de.pop();
// int a =Integer.parseInt(de.pop());
// int b =Integer.parseInt(de.pop());这样都报错 不知道为啥
1.上面的奇怪的错:把==换成equals就不报错了(LeetCode内置jdk问题,不能用双等号判断字符串相等)
2.re没啥用,每次都是重新计算完的值,可以直接赋值进去,返回栈中计算出来的最后一个被压入栈的值,和刚刚的返回re是一样的原理,re的最后计算出来的,是最后一个计算结果,然后把它压入栈中。
3.减法和除法要做一个操作,要不然顺序会有问题,变成后一个除前一个了。
- 自己正确代码:
class Solution {
public int evalRPN(String[] tokens) {
Deque<Integer> de = new LinkedList<>();
int re1=0,re2=0;
for(int i=0;i<tokens.length;i++){
if(tokens[i].equals("+")&&!de.isEmpty()){//把==换成了equals之后就不报错了
de.push(de.pop()+de.pop());
}else if(tokens[i].equals("-")&&!de.isEmpty()){
re1=de.pop(); re2= de.pop();//减法和除法的顺序需要考虑
de.push(re2-re1);
}else if(tokens[i].equals("*")&&!de.isEmpty()){
de.push(de.pop()*de.pop());
}else if(tokens[i].equals("/")&&!de.isEmpty()){
re1=de.pop(); re2= de.pop();
de.push(re2/re1);
}else{
de.push(Integer.parseInt(tokens[i]));
}
}
return de.pop();//返回栈中计算出来的最后一个被压入栈的值,和刚刚的返回re是一样的原理,re的最后计算出来的,也是最后一个计算结果
}
}
- 代码随想录代码,减法的处理很巧妙,除法和咱一样
class Solution {
public int evalRPN(String[] tokens) {
Deque<Integer> stack = new LinkedList();
for (String s : tokens) {
if ("+".equals(s)) {
stack.push(stack.pop() + stack.pop());
} else if ("-".equals(s)) {
stack.push(-stack.pop() + stack.pop());
} else if ("*".equals(s)) {
stack.push(stack.pop() * stack.pop());
} else if ("/".equals(s)) {
int temp1 = stack.pop();
int temp2 = stack.pop();
stack.push(temp2 / temp1);
} else {
stack.push(Integer.valueOf(s));
}
}
return stack.pop();
}
}