150. 逆波兰表达式求值(中等)
给你一个字符串数组
tokens
,表示一个根据 逆波兰表示法 表示的算术表达式。请你计算该表达式。返回一个表示表达式值的整数。
注意:
- 有效的算符为
'+'
、'-'
、'*'
和'/'
。- 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
- 两个整数之间的除法总是 向零截断 。
- 表达式中不含除零运算。
- 输入是一个根据逆波兰表示法表示的算术表达式。
- 答案及所有中间计算结果可以用 32 位 整数表示。
备注:向零截断是一种取整方式,也称为截断取整。这种取整方法将一个浮点数取整为最接近但小于它的整数。具体来说,对于正数,向零截断会取其整数部分;对于负数,向零截断会取其绝对值的整数部分并添加负号。例如,17 / 10 = 1,5 / 2 = 2,而 -9 / 4 = -2。
解法一、双向队列模拟栈
双向队列相关写在碎碎念里了,然后发现不用default,可以用isNumber判别
class Solution {
public static int evalRPN(String[] tokens) {
Deque<Integer> num = new ArrayDeque<>();
for(String s : tokens){
int len = num.size();
int a,b;
switch (s){
case "+":
a = num.removeLast();//第一个
b = num.removeLast();//第二个
num.add(a+b);
break;
case "-":
a = num.removeLast();//第一个
b = num.removeLast();//第二个
num.add(b-a);
break;
case"/":
a = num.removeLast();//第一个
b = num.removeLast();//第二个
num.add(b/a);
break;
case"*":
a = num.removeLast();//第一个
b = num.removeLast();//第二个
num.add(b*a);
break;
default:
int t = Integer.parseInt(s);
num.add(t);
break;
}
}
return num.pop();
}
}
227. 基本计算器 II(中等)
给你一个字符串表达式
s
,请你实现一个基本计算器来计算并返回它的值。整数除法仅保留整数部分。
你可以假设给定的表达式总是有效的。所有中间结果将在
[-231, 231 - 1]
的范围内。注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如
eval()
。
解法一、栈
这里不用if else结构是因为需要判断那个i==n-1,如果最后一个是数字,它依旧要考虑操作符号。
大体:对于第一个数字,默认的op是‘+’,对于其他的,可以用preSign记录下来。如果是数字,则不断循环,处理num值。如果不是数字或者是最后一个数字,则考虑操作符。这里不需要考虑空格,把空格跳掉即可,空格只是干扰项。
说起来这个A&&B||C的结构也很神奇,等效于(A&&B)||C,原来&和|是有优先级区分的(没细想过这个问题)
class Solution {
public static int calculate(String s) {
Deque<Integer> stack = new ArrayDeque<Integer>();
char preSign = '+';
int num = 0;
int n = s.length();
for (int i = 0; i < n; ++i) {
if (Character.isDigit(s.charAt(i))) {
num = num * 10 + s.charAt(i) - '0';
}
if (!Character.isDigit(s.charAt(i)) && s.charAt(i) != ' ' || i == n - 1) {
switch (preSign) {
case '+':
stack.push(num);
break;
case '-':
stack.push(-num);
break;
case '*':
stack.push(stack.pop() * num);
break;
default:
stack.push(stack.pop() / num);
}
preSign = s.charAt(i);
num = 0;
}
}
int ans = 0;
while (!stack.isEmpty()) {
ans += stack.pop();
}
return ans;
}
}
解法二、模拟
评论区看来的~↓
用last巧妙地记录和模拟了栈顶结构。
. - 力扣(LeetCode)
class Solution {
public:
int calculate(string s) {
int ret = 0, last = 0;
int value = 0;
char op = '+';
s += "+";
for(auto &c:s) {
if(isdigit(c))
value = value * 10 + (c - '0');
else if(c != ' ') {
if(op == '+') {
ret += value;
last = value;
}else if(op == '-') {
ret -= value;
last = -value;
}else if(op == '*') {
ret = ret - last + value * last;
last = value * last;
}else {
ret = ret - last + last/value;
last = last/value;
}
op = c;
value = 0;
}
}
return ret;
}
};
224. 基本计算器(困难)
给你一个字符串表达式
s
,请你实现一个基本计算器来计算并返回它的值。注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如
eval()
。示例 1:
输入:s = "1 + 1" 输出:2示例 2:
输入:s = " 2-1 + 2 " 输出:3示例 3:
输入:s = "(1+(4+5+2)-3)+(6+8)" 输出:23
解法一、栈+模拟
num存储数字,preOp参考227,i因为while是i++判断所以从-1开始,ops记录层数,默认一层为1,遇到(则push,遇到)则pop。因为只有加减,所以用1、-1即可
三个if(其中一个是if-else)的顺序很重要,第一个决定数字,第二个决定加减,第三个决定括号栈
易错点:①res的处理②preOp的设置③括号时ops的设置④左括号时把preOp置1
class Solution {
public static int calculate(String s) {
Deque<Integer> ops = new ArrayDeque<>();
int res = 0, num = 0, preOp = 1;
int i = -1, len = s.length();
ops.push(1);
//单指针循环遍历
while (++i < len) {
if (Character.isDigit(s.charAt(i))) {
num = num * 10 - '0' + s.charAt(i);
}
if (s.charAt(i) == '+' || s.charAt(i) == '-' || s.charAt(i) == ')'|| i == len-1 ) {
res += num * preOp * ops.element();
if (s.charAt(i) == '+') preOp = 1;
else preOp = -1;
num = 0;
}
if (s.charAt(i) == '(') {;
ops.push(ops.element() * preOp);
preOp = 1;
} else if (s.charAt(i) == ')') {
ops.pop();
}
}
return res;
}
}
解法二、改进版
空格跳过,处理数字,处理符号,清晰易懂。无论哪种做法,都要记得一开始push一个1的默认值,当作全部表达式最外层套了个()处理
class Solution {
public:
int calculate(string s) {
stack<char> st; // 存储正负号
int ans = 0, num = 0, op = 1;
st.push(op);
for (char c : s) {
if (c == ' ') continue;
else if (c >= '0') num = num * 10 - '0' + c;
else {
ans += op * num;
num = 0;
if (c == '+') op = st.top();
else if (c == '-') op = -st.top();
else if (c == '(') st.push(op); // 将括号前符号放入栈顶
else st.pop();
}
}
return ans + op * num;
}
};
碎碎念:
- 理解了这里用到的双向队列是ArrayDeque,头是0,尾/最后是末端(序号较大处)。push是从头(0)开始加,add是从尾开始加,和stack是有差别的。稍微地熟悉了element/peek之类的差别
- 150简单使用,227稍微有些复杂(中途会错意了),224注意一下脉络和思路再写会好很多,这个需要复刷