0%

algorithm-stack

简单

有效的括号

题目描述

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
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。


示例 1:

输入:s = "()"
输出:true
示例 2:

输入:s = "()[]{}"
输出:true
示例 3:

输入:s = "(]"
输出:false


提示:

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

题目解答

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

public class Solution {
public boolean isValid(String s) {
LinkedList<Character> stack = new LinkedList<>();
Map<Character, Character> map = new HashMap<Character, Character>() {
{
put('(', ')');
put('[', ']');
put('{', '}');
}
};

char[] charArray = s.toCharArray();
for (char c : charArray) {
if (map.containsKey(c)) {
stack.push(c);
} else {
if (stack.isEmpty()) {
return false;
}

Character k = stack.pop();
Character v = map.get(k);

if (v != c) {
return false;
}
}
}

return stack.isEmpty();
}

public static void main(String[] args) {
testCase1();
testCase2();
testCase3();
}

public static void testCase1() {
String s = "()";

boolean result = new Solution().isValid(s);
System.out.println(result);
}

public static void testCase2() {
String s = "()[]{}";

boolean result = new Solution().isValid(s);
System.out.println(result);
}

public static void testCase3() {
String s = "(]";

boolean result = new Solution().isValid(s);
System.out.println(result);
}
}

简化路径

题目描述

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
给你一个字符串 path ,表示指向某一文件或目录的 Unix 风格 绝对路径 (以 '/' 开头),请你将其转化为更加简洁的规范路径。

在 Unix 风格的文件系统中,一个点(.)表示当前目录本身;此外,两个点 (..) 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。任意多个连续的斜杠(即,'//')都被视为单个斜杠 '/' 。 对于此问题,任何其他格式的点(例如,'...')均被视为文件/目录名称。

请注意,返回的 规范路径 必须遵循下述格式:

始终以斜杠 '/' 开头。
两个目录名之间必须只有一个斜杠 '/' 。
最后一个目录名(如果存在)不能 以 '/' 结尾。
此外,路径仅包含从根目录到目标文件或目录的路径上的目录(即,不含 '.' 或 '..')。
返回简化后得到的 规范路径 。



示例 1:

输入:path = "/home/"
输出:"/home"
解释:注意,最后一个目录名后面没有斜杠。
示例 2:

输入:path = "/../"
输出:"/"
解释:从根目录向上一级是不可行的,因为根目录是你可以到达的最高级。
示例 3:

输入:path = "/home//foo/"
输出:"/home/foo"
解释:在规范路径中,多个连续斜杠需要用一个斜杠替换。
示例 4:

输入:path = "/a/./b/../../c/"
输出:"/c"


提示:

1 <= path.length <= 3000
path 由英文字母,数字,'.','/' 或 '_' 组成。
path 是一个有效的 Unix 风格绝对路径。

题目解答

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import java.util.LinkedList;

public class Solution {
public String simplifyPath(String path) {
int dotCount = 0;
LinkedList<Character> stack = new LinkedList<>();
char[] charArray = path.toCharArray();
for (char c : charArray) {
if (c == '.') {
dotCount++;
} else {
if (dotCount > 0) {
if (dotCount == 1 && stack.peek() == '/' && c == '/') {
// do nothing
} else if (dotCount == 2 && stack.peek() == '/' && c == '/') {
if (stack.size() > 1) {
stack.pop();

while (stack.size() > 1 && stack.peek() != '/') {
stack.pop();
}
}
} else {
for (int i = 0; i < dotCount; i++) {
stack.push('.');
}
}

dotCount = 0;
}

if (c == '/') {
if (stack.isEmpty() || stack.peek() != c) {
stack.push(c);
}
} else {
stack.push(c);
}
}
}

if (dotCount > 0) {
if (dotCount == 1 && stack.peek() == '/') {
// do nothing
} else if (dotCount == 2 && stack.peek() == '/') {
if (stack.size() > 1) {
stack.pop();

while (stack.size() > 1 && stack.peek() != '/') {
stack.pop();
}
}
} else {
for (int i = 0; i < dotCount; i++) {
stack.push('.');
}
}

dotCount = 0;
}

while (stack.size() > 1 && stack.peek() == '/') {
stack.pop();
}

StringBuilder sb = new StringBuilder();
while (!stack.isEmpty()) {
sb.insert(0, stack.pop());
}

return sb.toString();
}

public static void main(String[] args) {
testCase1();
testCase2();
testCase3();
testCase4();
testCase5();
testCase6();
testCase7();
testCase8();
testCase9();
testCase10();
}

public static void testCase1() {
String s = "/home/";

String result = new Solution().simplifyPath(s);
System.out.println(result);
}

public static void testCase2() {
String s = "/../";

String result = new Solution().simplifyPath(s);
System.out.println(result);
}

public static void testCase3() {
String s = "/home//foo/";

String result = new Solution().simplifyPath(s);
System.out.println(result);
}

public static void testCase4() {
String s = "/a/./b/../../c/";

String result = new Solution().simplifyPath(s);
System.out.println(result);
}

public static void testCase5() {
String s = "/...";

String result = new Solution().simplifyPath(s);
System.out.println(result);
}

public static void testCase6() {
String s = "/..";

String result = new Solution().simplifyPath(s);
System.out.println(result);
}

public static void testCase7() {
String s = "/..hidden";

String result = new Solution().simplifyPath(s);
System.out.println(result);
}

public static void testCase8() {
String s = "/.hidden";

String result = new Solution().simplifyPath(s);
System.out.println(result);
}

public static void testCase9() {
String s = "/hello../world";

String result = new Solution().simplifyPath(s);
System.out.println(result);
}

public static void testCase10() {
String s = "/hello./world";

String result = new Solution().simplifyPath(s);
System.out.println(result);
}
}

最小栈

题目描述

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
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。

实现 MinStack 类:

MinStack() 初始化堆栈对象。
void push(int val) 将元素val推入堆栈。
void pop() 删除堆栈顶部的元素。
int top() 获取堆栈顶部的元素。
int getMin() 获取堆栈中的最小元素。


示例 1:

输入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]

输出:
[null,null,null,null,-3,null,0,-2]

解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.

题目解答

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
import java.util.LinkedList;

class MinStack {
LinkedList<Integer> xStack;
LinkedList<Integer> minStack;

public MinStack() {
xStack = new LinkedList<Integer>();
minStack = new LinkedList<Integer>();
minStack.push(Integer.MAX_VALUE);
}

public void push(int x) {
xStack.push(x);
minStack.push(Math.min(minStack.peek(), x));
}

public void pop() {
xStack.pop();
minStack.pop();
}

public int top() {
return xStack.peek();
}

public int getMin() {
return minStack.peek();
}
}

下一个更大元素I

题目描述

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
nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。

给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。

对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j] 的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1 。

返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素 。



示例 1:

输入:nums1 = [4,1,2], nums2 = [1,3,4,2].
输出:[-1,3,-1]
解释:nums1 中每个值的下一个更大元素如下所述:
- 4 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
- 1 ,用加粗斜体标识,nums2 = [1,3,4,2]。下一个更大元素是 3 。
- 2 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
示例 2:

输入:nums1 = [2,4], nums2 = [1,2,3,4].
输出:[3,-1]
解释:nums1 中每个值的下一个更大元素如下所述:
- 2 ,用加粗斜体标识,nums2 = [1,2,3,4]。下一个更大元素是 3 。
- 4 ,用加粗斜体标识,nums2 = [1,2,3,4]。不存在下一个更大元素,所以答案是 -1 。


提示:

1 <= nums1.length <= nums2.length <= 1000
0 <= nums1[i], nums2[i] <= 104
nums1和nums2中所有整数 互不相同
nums1 中的所有整数同样出现在 nums2 中


进阶:你可以设计一个时间复杂度为 O(nums1.length + nums2.length) 的解决方案吗?

题目解答

1
//

TODO:下一个更大元素I

下一个更大元素II

题目描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
给定一个循环数组 nums ( nums[nums.length - 1] 的下一个元素是 nums[0] ),返回 nums 中每个元素的 下一个更大元素 。

数字 x 的 下一个更大的元素 是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1 。



示例 1:

输入: nums = [1,2,1]
输出: [2,-1,2]
解释: 第一个 1 的下一个更大的数是 2;
数字 2 找不到下一个更大的数;
第二个 1 的下一个最大的数需要循环搜索,结果也是 2。
示例 2:

输入: nums = [1,2,3,4,3]
输出: [2,3,4,-1,4]


提示:

1 <= nums.length <= 104
-109 <= nums[i] <= 109

题目解答

1
//

TODO:下一个更大元素II

逆波兰表达式求值

题目描述

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
44
45
46
47
48
49
50
51
52
53
54
给你一个字符串数组 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 + * 也可以依据次序计算出正确结果。
适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中

题目解答

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import java.util.LinkedList;

public class Solution {
public int evalRPN(String[] tokens) {
LinkedList<String> stack = new LinkedList<>();
for (String token : tokens) {
if ("+".equals(token)) {
String y = stack.pop();
String x = stack.pop();
int val = Integer.parseInt(x) + Integer.parseInt(y);
stack.push(val + "");
} else if ("-".equals(token)) {
String y = stack.pop();
String x = stack.pop();
int val = Integer.parseInt(x) - Integer.parseInt(y);
stack.push(val + "");
} else if ("*".equals(token)) {
String y = stack.pop();
String x = stack.pop();
int val = Integer.parseInt(x) * Integer.parseInt(y);
stack.push(val + "");
} else if ("/".equals(token)) {
String y = stack.pop();
String x = stack.pop();
int val = Integer.parseInt(x) / Integer.parseInt(y);
stack.push(val + "");
} else {
stack.push(token);
}
}

return Integer.parseInt(stack.pop());
}

public static void main(String[] args) {
testCase1();
testCase2();
testCase3();
}

public static void testCase1() {
String[] tokens = { "2", "1", "+", "3", "*" };

int result = new Solution().evalRPN(tokens);
System.out.println(result);
}

public static void testCase2() {
String[] tokens = { "4", "13", "5", "/", "+" };

int result = new Solution().evalRPN(tokens);
System.out.println(result);
}

public static void testCase3() {
String[] tokens = { "10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+" };

int result = new Solution().evalRPN(tokens);
System.out.println(result);
}
}

困难

基本计算器

题目描述

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
给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。

注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval() 。



示例 1:

输入:s = "1 + 1"
输出:2
示例 2:

输入:s = " 2-1 + 2 "
输出:3
示例 3:

输入:s = "(1+(4+5+2)-3)+(6+8)"
输出:23


提示:

1 <= s.length <= 3 * 105
s 由数字、'+'、'-'、'('、')'、和 ' ' 组成
s 表示一个有效的表达式
'+' 不能用作一元运算(例如, "+1" 和 "+(2 + 3)" 无效)
'-' 可以用作一元运算(即 "-1" 和 "-(2 + 3)" 是有效的)
输入中不存在两个连续的操作符
每个数字和运行的计算将适合于一个有符号的 32位 整数

题目解答

TODO:基本计算器

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
import java.util.LinkedList;

public class Solution {
public int calculate(String s) {
LinkedList<Integer> ops = new LinkedList<Integer>();
ops.push(1);
int sign = 1;

int ret = 0;
int n = s.length();
int i = 0;
while (i < n) {
if (s.charAt(i) == ' ') {
i++;
} else if (s.charAt(i) == '+') {
sign = ops.peek();
i++;
} else if (s.charAt(i) == '-') {
sign = -ops.peek();
i++;
} else if (s.charAt(i) == '(') {
ops.push(sign);
i++;
} else if (s.charAt(i) == ')') {
ops.pop();
i++;
} else {
long num = 0;
while (i < n && Character.isDigit(s.charAt(i))) {
num = num * 10 + s.charAt(i) - '0';
i++;
}
ret += sign * num;
}
}
return ret;
}
}
只想买包辣条