Skip to content

Commit def6a46

Browse files
committed
227
1 parent 749af16 commit def6a46

File tree

4 files changed

+326
-1
lines changed

4 files changed

+326
-1
lines changed

SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,4 +206,5 @@
206206
* [224*. Basic Calculator](leetcode-224-Basic-Calculator.md)
207207
* [225. Implement Stack using Queues](leetcode-225-Implement-Stack-using-Queues.md)
208208
* [226. Invert Binary Tree](leetcode-226-Invert-Binary-Tree.md)
209+
* [227. Basic Calculator II](leetcode-227-Basic-CalculatorII.md)
209210
* [更多](more.md)

leetcode-201-300.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,6 @@
5050

5151
<a href="leetcode-225-Implement-Stack-using-Queues.html">225. Implement Stack using Queues</a>
5252

53-
<a href="leetcode-226-Invert-Binary-Tree.html">226. Invert Binary Tree</a>
53+
<a href="leetcode-226-Invert-Binary-Tree.html">226. Invert Binary Tree</a>
54+
55+
<a href="leetcode-227-Basic-CalculatorII.html">227. Basic Calculator II</a>

leetcode-214-Shortest-Palindrome.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,32 @@ public String shortestPalindrome(String s) {
459459

460460
虽然这种方法 `AC` 了,但我觉得是侥幸的,我觉得即使每次取模,并不能保证不会出现 `hash` 冲突,只是当前的 `test case` 没有出现 `hash` 冲突。当然这是我的想法,并不是很确定,大家有其他想法欢迎和我交流。
461461

462+
感谢 @[franklinqin0](https://www.zhihu.com/people/franklinqin7) 指出,上边确认当前是否是回文串的时候,我们调用了 `isPalindromic` ,但超时了,这里的话我们还可以和它的逆置字符串进行比较。
463+
464+
```java
465+
public String shortestPalindrome(String s) {
466+
int n = s.length(), pos = -1;
467+
int b = 26; // 基数
468+
int pow = 1; // 为了方便计算倒置字符串的 hash 值
469+
char[] c = s.toCharArray();
470+
String rev = new StringBuilder(s).reverse().toString();
471+
int hash1 = 0, hash2 = 0;
472+
for (int i = 0; i < n; i++, pow = pow * b) {
473+
hash1 = hash1 * b + (c[i] - 'a' + 1);
474+
// 倒置字符串的 hash 值, 新增的字符要放到最高位
475+
hash2 = hash2 + (c[i] - 'a' + 1) * pow;
476+
if (hash1 == hash2) {
477+
if (s.substring(0, i + 1).equals(rev.substring(n - i - 1))) {
478+
pos = i;
479+
}
480+
}
481+
}
482+
return new StringBuilder(s.substring(pos + 1)).reverse() + s;
483+
}
484+
```
485+
486+
这样做的话就不会超时了,但如果分析时间复杂度的话其实是一样的,很神奇。
487+
462488
# 解法五
463489

464490
参考 [这里](https://leetcode.com/problems/shortest-palindrome/discuss/60113/Clean-KMP-solution-with-super-detailed-explanation)

leetcode-227-Basic-CalculatorII.md

Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
# 题目描述(中等难度)
2+
3+
![](https://windliang.oss-cn-beijing.aliyuncs.com/227.png)
4+
5+
基础计算器,只有加减乘除,正数,整数。
6+
7+
# 思路分析
8+
9+
[224 题](https://leetcode.wang/leetcode-224-Basic-Calculator.html) 已经介绍了两种通用的计算器的解法,一种是利用后缀表达式,一种是双栈,这里就直接在 [224 题](https://leetcode.wang/leetcode-224-Basic-Calculator.html) 的基础上改了,大家可以先去做一下。
10+
11+
# 解法一 后缀表达式
12+
13+
[150 题](https://leetcode.wang/leetcode-150-Evaluate-Reverse-Polish-Notation.html) 已经写了后缀表达式的求值,这里的话我们主要是写中缀表达式转后缀表达式,下边是规则。
14+
15+
1)如果遇到操作数,我们就直接将其加入到后缀表达式。
16+
17+
2)如果遇到左括号,则我们将其放入到栈中。
18+
19+
3)如果遇到一个右括号,则将栈元素弹出,将弹出的操作符加入到后缀表达式直到遇到左括号为止,接着将左括号弹出,但不加入到结果中。
20+
21+
4)如果遇到其他的操作符,如(“+”, “-”)等,从栈中弹出元素将其加入到后缀表达式,直到栈顶的元素优先级比当前的优先级低(或者遇到左括号或者栈为空)为止。弹出完这些元素后,最后将当前遇到的操作符压入到栈中。
22+
23+
5)如果我们读到了输入的末尾,则将栈中所有元素依次弹出。
24+
25+
这道题比较简单,不用考虑括号,只需要判断当前操作符和栈顶操作符的优先级。
26+
27+
```java
28+
//op1 > op2 的时候返回 true, 其他情况都返回 false
29+
private boolean compare(String op1, String op2) {
30+
if (op1.equals("*") || op1.equals("/")) {
31+
return op2.equals("+") || op2.equals("-");
32+
}
33+
return false;
34+
}
35+
```
36+
37+
下边是整体的代码,供参考。
38+
39+
```java
40+
public int calculate(String s) {
41+
String[] polish = getPolish(s); // 转后缀表达式
42+
return evalRPN(polish);
43+
}
44+
45+
// 中缀表达式转后缀表达式
46+
private String[] getPolish(String s) {
47+
List<String> res = new ArrayList<>();
48+
Stack<String> stack = new Stack<>();
49+
char[] array = s.toCharArray();
50+
int n = array.length;
51+
int temp = -1; // 累加数字,-1 表示当前没有数字
52+
for (int i = 0; i < n; i++) {
53+
if (array[i] == ' ') {
54+
continue;
55+
}
56+
// 遇到数字
57+
if (isNumber(array[i])) {
58+
// 进行数字的累加
59+
if (temp == -1) {
60+
temp = array[i] - '0';
61+
} else {
62+
temp = temp * 10 + array[i] - '0';
63+
}
64+
} else {
65+
// 遇到其它操作符,将数字加入到结果中
66+
if (temp != -1) {
67+
res.add(temp + "");
68+
temp = -1;
69+
}
70+
// 遇到操作符将栈中的操作符加入到结果中
71+
while (!stack.isEmpty()) {
72+
// 栈顶优先级更低就结束
73+
if (compare(array[i]+"",stack.peek())) {
74+
break;
75+
}
76+
res.add(stack.pop());
77+
}
78+
// 当前操作符入栈
79+
stack.push(array[i] + "");
80+
81+
}
82+
}
83+
// 如果有数字,将数字加入到结果
84+
if (temp != -1) {
85+
res.add(temp + "");
86+
}
87+
// 栈中的其他元素加入到结果
88+
while (!stack.isEmpty()) {
89+
res.add(stack.pop());
90+
}
91+
String[] sArray = new String[res.size()];
92+
// List 转为 数组
93+
for (int i = 0; i < res.size(); i++) {
94+
sArray[i] = res.get(i);
95+
}
96+
return sArray;
97+
}
98+
99+
private boolean compare(String op1, String op2) {
100+
if (op1.equals("*") || op1.equals("/")) {
101+
return op2.equals("+") || op2.equals("-");
102+
}
103+
return false;
104+
}
105+
106+
// 下边是 150 题的代码,求后缀表达式的值
107+
public int evalRPN(String[] tokens) {
108+
Stack<String> stack = new Stack<>();
109+
for (String t : tokens) {
110+
if (isOperation(t)) {
111+
int a = stringToNumber(stack.pop());
112+
int b = stringToNumber(stack.pop());
113+
int ans = eval(b, a, t.charAt(0));
114+
stack.push(ans + "");
115+
} else {
116+
stack.push(t);
117+
}
118+
}
119+
return stringToNumber(stack.pop());
120+
}
121+
122+
private int eval(int a, int b, char op) {
123+
switch (op) {
124+
case '+':
125+
return a + b;
126+
case '-':
127+
return a - b;
128+
case '*':
129+
return a * b;
130+
case '/':
131+
return a / b;
132+
}
133+
return 0;
134+
}
135+
136+
private int stringToNumber(String s) {
137+
int sign = 1;
138+
int start = 0;
139+
if (s.charAt(0) == '-') {
140+
sign = -1;
141+
start = 1;
142+
}
143+
int res = 0;
144+
for (int i = start; i < s.length(); i++) {
145+
res = res * 10 + s.charAt(i) - '0';
146+
}
147+
return res * sign;
148+
}
149+
150+
private boolean isNumber(char c) {
151+
return c >= '0' && c <= '9';
152+
}
153+
154+
private boolean isOperation(String t) {
155+
return t.equals("+") || t.equals("-") || t.equals("*") || t.equals("/");
156+
}
157+
```
158+
159+
# 解法二 双栈
160+
161+
规则如下。
162+
163+
1. 使用两个栈,`stack0` 用于存储操作数,`stack1` 用于存储操作符
164+
2. 从左往右扫描,遇到操作数入栈 `stack0`
165+
3. 遇到操作符时,如果当前优先级低于或等于栈顶操作符优先级,则从 `stack0` 弹出两个元素,从 `stack1` 弹出一个操作符,进行计算,将结果并压入`stack0`,继续与栈顶操作符的比较优先级。
166+
4. 如果遇到操作符高于栈顶操作符优先级,则直接入栈 `stack1`
167+
5. 遇到左括号,直接入栈 `stack1`
168+
6. 遇到右括号,则从 `stack0` 弹出两个元素,从 `stack1` 弹出一个操作符进行计算,并将结果加入到 `stack0` 中,重复这步直到遇到左括号
169+
170+
同样的不需要考虑括号,会变得更简单一些。
171+
172+
```java
173+
public int calculate(String s) {
174+
char[] array = s.toCharArray();
175+
int n = array.length;
176+
Stack<Integer> num = new Stack<>();
177+
Stack<Character> op = new Stack<>();
178+
int temp = -1;
179+
for (int i = 0; i < n; i++) {
180+
if (array[i] == ' ') {
181+
continue;
182+
}
183+
// 数字进行累加
184+
if (isNumber(array[i])) {
185+
if (temp == -1) {
186+
temp = array[i] - '0';
187+
} else {
188+
temp = temp * 10 + array[i] - '0';
189+
}
190+
} else {
191+
// 将数字入栈
192+
if (temp != -1) {
193+
num.push(temp);
194+
temp = -1;
195+
}
196+
// 遇到操作符
197+
while (!op.isEmpty()) {
198+
//遇到更低优先级的话就结束
199+
if (compare(array[i], op.peek())) {
200+
break;
201+
}
202+
// 不停的出栈,进行运算,并将结果再次压入栈中
203+
int num1 = num.pop();
204+
int num2 = num.pop();
205+
num.push(eval(num1, num2, op.pop()));
206+
}
207+
// 当前运算符入栈
208+
op.push(array[i]);
209+
210+
}
211+
}
212+
if (temp != -1) {
213+
num.push(temp);
214+
}
215+
// 将栈中的其他元素继续运算
216+
while (!op.isEmpty()) {
217+
int num1 = num.pop();
218+
int num2 = num.pop();
219+
num.push(eval(num1, num2, op.pop()));
220+
}
221+
return num.pop();
222+
}
223+
224+
private boolean compare(char op1, char op2) {
225+
if(op1 == '*' || op1 == '/'){
226+
return op2 == '+' || op2 == '-';
227+
}
228+
return false;
229+
}
230+
231+
private boolean isNumber(char c) {
232+
return c >= '0' && c <= '9';
233+
}
234+
private int eval(int a, int b, char op) {
235+
switch (op) {
236+
case '+':
237+
return a + b;
238+
case '-':
239+
return b - a;
240+
case '*':
241+
return a * b;
242+
case '/':
243+
return b / a;
244+
}
245+
return 0;
246+
}
247+
```
248+
249+
[224 题](https://leetcode.wang/leetcode-224-Basic-Calculator.html) 一样需要注意减法和除法,由于使用了栈,所以算的时候两个数字要反一下。
250+
251+
# 解法三
252+
253+
分享一下 [这里](https://leetcode.com/problems/basic-calculator-ii/discuss/63003/Share-my-java-solution) 的解法,属于专门针对这道题的解法。
254+
255+
把减法、乘法、除法在遍历过程中将结果计算出来,最后将所有结果累加。
256+
257+
```java
258+
public int calculate(String s) {
259+
int len;
260+
if(s==null || (len = s.length())==0) return 0;
261+
Stack<Integer> stack = new Stack<Integer>();
262+
int num = 0;
263+
char sign = '+';
264+
for(int i=0;i<len;i++){
265+
if(Character.isDigit(s.charAt(i))){
266+
num = num*10+s.charAt(i)-'0';
267+
}
268+
if((!Character.isDigit(s.charAt(i)) &&' '!=s.charAt(i)) || i==len-1){
269+
if(sign=='-'){
270+
stack.push(-num);
271+
}
272+
if(sign=='+'){
273+
stack.push(num);
274+
}
275+
if(sign=='*'){
276+
stack.push(stack.pop()*num);
277+
}
278+
if(sign=='/'){
279+
stack.push(stack.pop()/num);
280+
}
281+
sign = s.charAt(i);
282+
num = 0;
283+
}
284+
}
285+
286+
int re = 0;
287+
for(int i:stack){
288+
re += i;
289+
}
290+
return re;
291+
}
292+
```
293+
294+
#
295+
296+
这道题的话只是抽离了计算器的一部分功能,只要学会了通用的方法,很快就能写出来。

0 commit comments

Comments
 (0)