有效的括号(括号匹配)

来源:力扣(Leetcode)
链接:https://leetcode-cn.com/problems/valid-parentheses/

问题叙述

给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

  • 左括号必须用相同类型的右括号闭合。
  • 左括号必须以正确的顺序闭合。

示例 1

输入:s = “()”
输出:true

示例 2:

输入:s = “()[]{}”
输出:true

示例 3:

输入:s = “(]”
输出:false

示例 4:

输入:s = “([)]”
输出:false

示例 5:

输入:s = “{[]}”
输出:true

提示

1 <= s.length <= 104
s 仅由括号 ‘()[]{}’ 组成

分析

栈的先入口处的特点刚好与本题的括号匹配特点一致
如果遇到左括号则入栈,遇到右括号应将对应的栈顶左括号出栈
遍历结束之后,栈为空则符合条件

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class Solution {
public boolean isValid(String s) {
Stack<Character> stack = new Stack();
char ch;
for (int i = 0; i < s.length(); i++) {
ch = s.charAt(i);
if (ch == '[' || ch == '{' || ch == '(')
stack.push(ch);
else if (ch == '}') {
if (stack.isEmpty() || stack.peek() != '{') return false;
else stack.pop();
} else if (ch == ']') {
if (stack.isEmpty() || stack.peek() != '[') return false;
else stack.pop();
} else if (ch == ')') {
if (stack.isEmpty() || stack.peek() != '(') return false;
else stack.pop();
}
}
return stack.isEmpty();
}
}

//改进版
class Solution {
public boolean isValid(String s) {
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() || ch != stack.peek())
return false;
else stack.pop();
}
return stack.isEmpty();
}
}

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

来源:力扣(Leetcode)
链接:https://leetcode-cn.com/problems/remove-all-adjacent-duplicates-in-string/

问题叙述

给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。

示例

输入:“abbaca”
输出:“ca”

解释

例如,在 “abbaca” 中,我们可以删除 “bb” 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 “aaca”,其中又只有 “aa” 可以执行重复项删除操作,所以最后的字符串为 “ca”。

提示

1 <= S.length <= 20000
S 仅由小写英文字母组成。

分析

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Solution {
public String removeDuplicates(String s) {
Stack<Character> stack = new Stack<>();
char c;
for (int i = 0; i < s.length(); i++) {
c = s.charAt(i);
if (stack.isEmpty() || stack.peek() != c)
stack.push(c);
else stack.pop();
}
String str = "";
while (!stack.isEmpty())
str = stack.pop() + str;//注意是逆序相加
return str;
}
}

//双指针
class Solution {
public String removeDuplicates(String s) {
char[] str = s.toCharArray();
int fast = 0;
int slow = -1;
for (; fast < str.length; fast++) {
if (slow == -1 || str[slow] != str[fast])
str[++slow] = str[fast];
else slow--;
}
return String.valueOf(str, 0, slow + 1);
}
}

逆波兰表达式求值

来源:力扣(Leetcode)
链接:https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/

问题叙述

根据 逆波兰表达式,求表达式的值。
有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
注意 两个整数之间的除法只保留整数部分
可以保证给定的逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 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. 去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。
  2. 适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中

分析

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < tokens.length; i++) {
if ("+".equals(tokens[i])) {
stack.push(stack.pop() + stack.pop());
} else if ("-".equals(tokens[i])) {
stack.push(-stack.pop() + stack.pop());
} else if ("*".equals(tokens[i])) {
stack.push(stack.pop() * stack.pop());
} else if ("/".equals(tokens[i])) {
int a = stack.pop();
int b = stack.pop();
stack.push(b / a);
} else stack.push(Integer.parseInt(tokens[i]));
}
return stack.peek();
}
}

滑动窗口的最大值

来源:力扣(Leetcode)
链接:https://leetcode-cn.com/problems/sliding-window-maximum/

问题叙述

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值。

示例 1

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]

解释:
滑动窗口的位置 最大值


[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7

示例 2

输入:nums = [1], k = 1
输出:[1]

提示

1 <= nums.length <= 105
-104 <= nums[i] <= 104
1 <= k <= nums.length

分析

将每一个元素添加到队列中 只要新元素大于队尾元素(这种情况队尾元素不可能是滑动窗口内的最大值),
那么队尾元素出队,也就是我们维护的是一个单调递减的队列
又由于处在队首的元素是最早进队的,所以我们还需要判断,队首元素是否还在滑动窗口内

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int[] result = new int[nums.length - k + 1];
LinkedList<Integer> deque = new LinkedList<>();
for (int i = 0; i < nums.length; i++) {
while (!deque.isEmpty() && nums[i] >= nums[deque.getLast()])
deque.pollLast();
deque.addLast(i);
if (deque.peek() < i - k + 1)
deque.poll();
if (i + 1 >= k)
result[i + 1 - k] = nums[deque.peek()];
}
return result;
}
}