给定一个只包括 '('
,')'
,'{'
,'}'
,'['
,']'
的字符串 s
,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
示例 1:
输入:s = "()" 输出:true
示例 2:
输入:s = "()[]{}" 输出:true
示例 3:
输入:s = "(]" 输出:false
提示:
1 <= s.length <= 104
s
仅由括号'()[]{}'
组成
类似这种对称匹配的题目,很容易想到利用栈的特殊性质解决题目。。按照正常思路,只要每次遍历的字符是左括号,直接压入。如果是右括号,就和栈顶元素比较看是否匹配。如果在遍历到右括号时,栈已经空了,没有栈顶元素,则字符串无效。如果遍历完栈不空,则字符串无效。如果遍历完栈正好空了,则字符串有效。
class Solution {
public boolean isValid(String s) {
if (s.length() % 2 != 0) return false;
Stack<Character> stack = new Stack<>();
char ch;
for (int i = 0; i < s.length(); i++) {
ch = s.charAt(i);
if (ch == '(') stack.push(')');
else if (ch == '{') stack.push('}');
else if (ch == '[') stack.push(']');
else if (stack.isEmpty() || stack.peek() != ch) return false;
else stack.pop();
}
return stack.isEmpty();
}
}
代码还用了两个优化。一是剪枝,如果字符串中字符不是偶数个,那肯定是无效字符串,不用匹配了。二是匹配字符串的优化,不压入左括号,而是压入对应右括号,这样匹配时直接看栈顶元素与遍历元素相不相等即可,不然又要细分三种情况,或者要用Map匹配。
给出由小写字母组成的字符串 S
,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
示例:
输入:"abbaca" 输出:"ca" 解释: 例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。
提示:
1 <= S.length <= 20000
S
仅由小写英文字母组成。
这题比上题更容易想到用栈了:
class Solution {
public String removeDuplicates(String s) {
Stack<Character> stack = new Stack<>();
char ch;
for (int i = 0; i < s.length(); i++) {
ch = s.charAt(i);
if (stack.isEmpty() || ch != stack.peek()){
stack.push(ch);
}
else stack.pop();
}
StringBuilder sb = new StringBuilder();
while(!stack.isEmpty()){
sb.append(stack.pop());
}
return sb.reverse().toString();
}
}
遍历字符等于栈顶字符,就把栈顶字符弹出来,否则则压进去,解决。要注意的是遍历结束后要把栈内元素弹出来作为字符串输出,但此时元素顺序是相反的。如果不想再用个栈倒一手,还是用下StringBuffer的reverse函数。或者干脆用Deque模拟栈?
后面看了其他人的解法,发现String也是可以的:
while (!deque.isEmpty()) {
str = stack.pop() + str;
}
可见String类的各种骚操作还是不太熟,要多多练习!
根据 逆波兰表示法,求表达式的值。
有效的算符包括 +
、-
、*
、/
。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
注意 两个整数之间的除法只保留整数部分。
可以保证给定的逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
示例 1:
输入:tokens = ["2","1","+","3","*"] 输出:9 解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
示例 2:
输入:tokens = ["4","13","5","/","+"] 输出:6 解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
示例 3:
输入:tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"] 输出:22 解释:该算式转化为常见的中缀算术表达式为: ((10 * (6 / ((9 + 3) * -11))) + 17) + 5 = ((10 * (6 / (12 * -11))) + 17) + 5 = ((10 * (6 / -132)) + 17) + 5 = ((10 * 0) + 17) + 5 = (0 + 17) + 5 = 17 + 5 = 22
提示:
1 <= tokens.length <= 104
tokens[i]
是一个算符("+"
、"-"
、"*"
或"/"
),或是在范围[-200, 200]
内的一个整数
逆波兰表达式:
逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。
- 平常使用的算式则是一种中缀表达式,如
( 1 + 2 ) * ( 3 + 4 )
。 - 该算式的逆波兰表达式写法为
( ( 1 2 + ) ( 3 4 + ) * )
。
逆波兰表达式主要有以下两个优点:
- 去掉括号后表达式无歧义,上式即便写成
1 2 + 3 4 + *
也可以依据次序计算出正确结果。 - 适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
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();
}
}
题目最后解释逆波兰表达式的时候已经把怎么操作写出来了,照着写就行,没难度。