代码随想录算法训练营第十一天| 20. 有效的括号,1047. 删除字符串中的所有相邻重复项,150. 逆波兰表达式求值

题目与题解

20. 有效的括号

题目链接:20. 有效的括号

代码随想录题解:20. 有效的括号

视频讲解:栈的拿手好戏!| LeetCode:20. 有效的括号_哔哩哔哩_bilibili

解题思路:

        经典栈应用题,以前做过好几次了。将括号依次压入栈中,如果栈顶括号与下一个括号配对,则将栈顶元素弹出;反之则压入栈。遍历结束后再判断栈是否为空即可。

class Solution {
    public boolean isValid(String s) {
		Stack<Character> stack = new Stack<>();
		for (int i = 0; i < s.length(); i++) {
			if (stack.isEmpty()) {
				stack.push(s.charAt(i));
				continue;
			}
			if ('(' == stack.peek() && ')' == s.charAt(i)
			|| '{' == stack.peek() && '}' == s.charAt(i)
			|| '[' == stack.peek() && ']' == s.charAt(i)) {
				stack.pop();
			} else {
				stack.push(s.charAt(i));
			}
		}
		return stack.isEmpty();
    }
}

看完代码随想录之后的想法 

        基本思路差不多,分析很全面,但是做法有点不一样。随想录给出的括号不匹配情况如下:

第一种情况:已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false

第二种情况:遍历字符串匹配的过程中,发现栈里没有要匹配的字符。所以return false

第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号return false

        具体做法是,如果遍历得到的元素是左括号,则将应该匹配的右括号压入栈中;如果遍历元素是右括号,则判断该元素与当前的栈顶元素是否相同,相同则弹出,否则就直接返回失败。最后判断栈是否为空即可。

        相比之下,这样写会更快的发现输入是否符合条件。因为一旦碰到右括号不匹配的情况,就说明一定没有符合要求的左括号,可以直接返回错误了。

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();
    }
}

遇到的困难

        无

1047. 删除字符串中的所有相邻重复项

题目链接:​​​​​​​1047. 删除字符串中的所有相邻重复项

代码随想录题解:​​​​​​​1047. 删除字符串中的所有相邻重复项

视频讲解:栈的好戏还要继续!| LeetCode:1047. 删除字符串中的所有相邻重复项_哔哩哔哩_bilibili

解题思路:

        跟上一题有些类似,同样是匹配问题,碰到符合匹配条件的就将元素弹出栈,不匹配就压栈。这一题中匹配条件就是栈顶元素和遍历到的元素是否相同。遍历完成后,由于栈中元素的顺序是倒序,还需要把它正序输出。

class Solution {
    public String removeDuplicates(String s) {
		Stack<Character> stack = new Stack<>();
		for (int i = 0; i < s.length(); i++) {
			if (stack.isEmpty()) {
				stack.push(s.charAt(i));
			} else {
				if (stack.peek() == s.charAt(i)) {
					stack.pop();
				} else {
					stack.push(s.charAt(i));
				}
			}
		}
		char[] result = new char[stack.size()];
		for (int i = stack.size() - 1; i >= 0; i--) {
			result[i] = stack.pop();
		}
		return new String(result);
    }
}

看完代码随想录之后的想法 

        自己的写法是随想录java方法的第一种写法,但是写的有些重复和复杂了,这题可以把需要压栈的情况写在一起,用stack.isEmpty() || stack.peek() != s.charAt(i),就可以少写一层判断。

        另外,随想录还给出了两种写法,也很有启发性。

        一个是用StringBuilder或者StringBuffer这种可以修改内容的字符串类直接作为栈,一边处理输入字符的同时,一边就将正确的元素加入,就不需要最后一步再将栈中元素倒序出来,给一个新的字符串赋值;

        还有一种是用双指针法,方法类似之前的移除元素,快慢指针先一起出发,碰到前后字符相同时,快指针前进一步,慢指针不动;每次循环之前,都将快指针对应的值赋予慢指针。最后将[0,slow]之间的字符串返回即可。

遇到的困难

        无

150. 逆波兰表达式求值

题目链接:​​​​​​​150. 逆波兰表达式求值

代码随想录题解:​​​​​​​150. 逆波兰表达式求值

视频讲解:栈的最后表演! | LeetCode:150. 逆波兰表达式求值_哔哩哔哩_bilibili

解题思路:

        逆波兰表达式其实就是将普通的算式,按照实际计算顺序排布的一种公式。这题也是栈的经典应用题,本身没有什么太多的算法,更多是要模拟这样的过程。

        实际计算时,先设置一个栈,用于存放输入的数字或计算结果。对输入的字符串数组进行遍历,如果字符串是数字,则直接压栈,如果字符串是符号,则要将栈顶的两个元素取出来,对其根据符号计算后,再将计算结果压栈。直到遍历完成,栈中应只剩一个元素,该元素就是最后的计算结果,弹出即可。

        写的时候需要注意,因为出栈顺序和压栈顺序是相反的,因此涉及到减法和除法这两种计算时,栈顶的元素才是被减数和被除数,栈顶下一层的元素是减数和被减数。

        另外,由于输入都是字符串格式,所以需要将其转换为数字类型才能计算。Java自带的Integer.parseInt()方法可以直接转化。

class Solution {
    public int evalRPN(String[] tokens) {
		Stack<String> stack = new Stack<>();
		for (int i = 0; i < tokens.length; i++) {
			if ("+".equals(tokens[i])) {
				int a = Integer.parseInt(stack.pop());
				int b = Integer.parseInt(stack.pop());
				stack.push(""+(b+a));
			} else if ("-".equals(tokens[i])) {
				int a = Integer.parseInt(stack.pop());
				int b = Integer.parseInt(stack.pop());
				stack.push(""+(b-a));
			} else if ("*".equals(tokens[i])) {
				int a = Integer.parseInt(stack.pop());
				int b = Integer.parseInt(stack.pop());
				stack.push(""+(b*a));
			} else if ("/".equals(tokens[i])) {
				int a = Integer.parseInt(stack.pop());
				int b = Integer.parseInt(stack.pop());
				stack.push(""+(b/a));
			} else {
				stack.push(tokens[i]);
			}
		}
		return Integer.parseInt(stack.pop());
    }
}

看完代码随想录之后的想法 

        还是代码更简洁一点。

        首先,可以设置一个元素类型为Integer的栈,压栈的时候用Integer.valueOf()将其实际值压入即可,如果读取到的是符号,就可以直接通过判断进行计算,无需将其压栈。

        对于加法和乘法,数字的顺序不影响计算,无需临时变量保存栈顶两个元素,直接用stack.pop()相加或相乘即可;减法也是一种特殊的加法,用-stack.pop()+stack.pop()即可实现;只有除法需要把栈顶数字临时取出来再计算。

class Solution {
    public int evalRPN(String[] tokens) {
        Deque<Integer> stack = new LinkedList();
        for (String s : tokens) {
            if ("+".equals(s)) {        // leetcode 内置jdk的问题,不能使用==判断字符串是否相等
                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();
    }
}

遇到的困难

        我一开始在本地跑的很好,一次就通过了,但是提交上去的时候,总是抛出异常,说‘*’的值不是数字类型,不能放入Integer.parseInt。因为本地几个测试用例都通过了,我就觉得很神奇,想来想去也没想出来,所以先瞄了一眼答案,发现在判断输入是否为符号时,我用的是==,而答案用了equals。改掉以后果然就对了。

        之前学java的时候隐约记得equals和==有一点区别,但平常没有碰到什么错误,所以两种都有在用。百度了一下两者之间的区别如下:

  • “==”是一个运算符,用于比较变量或对象的值,当比较两个基本数据类型时,它比较的是它们的值是否相等;当比较两个引用数据类型时,它比较的是这两个对象的内存地址是否相同。
  • equals()是Object类中的一个方法,用于比较两个对象是否相等,如果一个类没有重写equals()方法,那么它调用的实际上是“==”方法,比较的是对象的内存地址;如果一个类重写了equals()方法,通常是为了比较两个对象的内容是否相等,例如,String类就重写了equals()方法,用于比较两个字符串的内容是否相同。 

        所以,当我在这里用==时,实际比较的是常量字符串“*”和当前字符String[i]的引用对象是否一样,那结果肯定是不一样的,所以判断就出错了。而用equals就只是判断两者的值是否相等,符合要求。今后一定要多注意。 

今日收获

        强化了一下栈的应用学习,了解了java中==和equals的区别。题目本身都不难,难点在于如何看到题目能想到用栈去解决。栈是最适合匹配问题的,希望日后一看到就能会用吧。

  • 19
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值