题目:
给定一个只包括
'('
,')'
,'{'
,'}'
,'['
,']'
的字符串s
,判断字符串是否有效。有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
示例 1:
输入:s = "()" 输出:true示例 2:
输入:s = "()[]{}" 输出:true示例 3:
输入:s = "(]" 输出:false提示:
1 <= s.length <= 104
s
仅由括号'()[]{}'
组成
自己看到题目的第一想法
需要使用栈来判断上一个括号是谁, 一旦出现不匹配的就表示匹配失败.
看完代码随想录之后的想法
大体思路是一致的, 但是我们在解题之前应该先想明白匹配过程中可能遇到的情况.
一共有下面这三种失败的情况:
1. 当全部遍历完成之后, 发现栈中有元素, 说明有未匹配的符号, 因此不匹配.
2. 遍历的过程中发现有匹配不上的符号, 因此不匹配.
3. 当遍历过程中发现当前右括号没有可以匹配的符号, 因此不匹配.
class Solution {
public boolean isValid(String s) {
if (s == null) {
return false;
}
Deque<Character> symbolChars = new LinkedList<>();
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '('
|| s.charAt(i) == '['
|| s.charAt(i) == '{') {
symbolChars.add(s.charAt(i));
} else {
switch(s.charAt(i)) {
case ')': {
if (symbolChars.isEmpty() || '(' != symbolChars.pollLast()) {
return false;
}
break;
}
case ']': {
if (symbolChars.isEmpty() || '[' != symbolChars.pollLast()) {
return false;
}
break;
}
case '}': {
if (symbolChars.isEmpty() || '{' != symbolChars.pollLast()) {
return false;
}
break;
}
}
}
}
return symbolChars.isEmpty();// 这里我每一次都会漏掉. 需要判断没有匹配失败, 但是没有可匹配符号的情况
}
}
自己实现过程中遇到哪些困难
对于 Deque 接口的使用还不是很熟练.
[LeetCode] 1047. 删除字符串中的所有相邻重复项
[LeetCode] 1047. 删除字符串中的所有相邻重复项 文章解释
[LeetCode] 1047. 删除字符串中的所有相邻重复项 视频解释
题目:
给出由小写字母组成的字符串
S
,重复项删除操作会选择两个相邻且相同的字母,并删除它们。在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
示例:
输入:"abbaca" 输出:"ca" 解释: 例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。提示:
1 <= S.length <= 20000
S
仅由小写英文字母组成。
自己看到题目的第一想法
1. 看错题目了, 以为是当出现相邻重复的全部删除, 因此做了一个简单的版本.
2. 很适合快慢指针的思路, 只要发现快慢指针相同时, 就将快指针往前走一步, 慢指针回退一步.
3. 但是我怎么怎么都写不出来呢?
4. 算了, 还是用栈来实现吧.
看完代码随想录之后的想法
可以用 ArrayDeque 来保存结果, 也可以用 StringBuilder 来保存结果, StringBuilder 可以省去用 ArrayDeque 需要把数据转换成字符串的步骤.
// 解法一: 使用栈来去重
class Solution {
public String removeDuplicates(String s) {
if (s.length() <= 1) {
return s;
}
StringBuilder strBuilder = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
if (strBuilder.length() == 0
|| s.charAt(i) != strBuilder.charAt(strBuilder.length() - 1)) {
strBuilder.append(s.charAt(i));
} else {
strBuilder.deleteCharAt(strBuilder.length() - 1);
}
}
return strBuilder.toString();
}
}
// 解法一: 使用快慢指针
class Solution {
public String removeDuplicates(String s) {
if (s == null || s.length() <= 1) {
return s;
}
char[] chars = s.toCharArray();
int slowIndex = 0;
for (int fastIndex = 1; fastIndex < chars.length; fastIndex++) {
if (slowIndex == -1 || chars[slowIndex] != chars[fastIndex]) {
chars[++slowIndex] = chars[fastIndex];
} else {
slowIndex--;
}
}
return new String(chars, 0, slowIndex + 1);
}
}
题目:
给你一个字符串数组
tokens
,表示一个根据 逆波兰表示法 表示的算术表达式。请你计算该表达式。返回一个表示表达式值的整数。
注意:
- 有效的算符为
'+'
、'-'
、'*'
和'/'
。- 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
- 两个整数之间的除法总是 向零截断 。
- 表达式中不含除零运算。
- 输入是一个根据逆波兰表示法表示的算术表达式。
- 答案及所有中间计算结果可以用 32 位 整数表示。
示例 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) {
ArrayDeque<Integer> result = new ArrayDeque<>();
for (int i = 0; i < tokens.length; i++) {
if ("+".equals(tokens[i])) {
int second = result.removeLast();
int first = result.removeLast();
result.add(first + second);
} else if ("-".equals(tokens[i])) {
int second = result.removeLast();
int first = result.removeLast();
result.add(first - second);
} else if ("*".equals(tokens[i])) {
int second = result.removeLast();
int first = result.removeLast();
result.add(first * second);
} else if ("/".equals(tokens[i])) {
int second = result.removeLast();
int first = result.removeLast();
result.add(first / second);
} else {
result.add(Integer.parseInt(tokens[i]));
}
}
return result.getLast();
}
}
// 快慢指针, 这个版本效率没有更高, 也许是因为有大量的字符串操作
class Solution {
public int evalRPN(String[] tokens) {
int slowIndex = 0;
int fastIndex = 0;
while (fastIndex < tokens.length) {
if ("+".equals(tokens[fastIndex]) || "-".equals(tokens[fastIndex])
|| "*".equals(tokens[fastIndex]) || "/".equals(tokens[fastIndex])) {
int first = Integer.parseInt(tokens[slowIndex - 2]);
int second = Integer.parseInt(tokens[slowIndex - 1]);
switch (tokens[fastIndex]) {
case "+": {
tokens[slowIndex - 2] = first + second + "";
break;
}
case "-": {
tokens[slowIndex - 2] = first - second + "";
break;
}
case "*": {
tokens[slowIndex - 2] = first * second + "";
break;
}
case "/": {
tokens[slowIndex - 2] = first / second + "";
break;
}
}
slowIndex--;
fastIndex++;
} else {
tokens[slowIndex++] = tokens[fastIndex++];
}
}
return Integer.parseInt(tokens[slowIndex - 1]);
}
}