目录
一,题目描述
英文描述
Given a string s which represents an expression, evaluate this expression and return its value.
The integer division should truncate toward zero.
You may assume that the given expression is always valid. All intermediate results will be in the range of [-231, 231 - 1].
Note: You are not allowed to use any built-in function which evaluates strings as mathematical expressions, such as eval().
中文描述
给你一个字符串表达式
s
,请你实现一个基本计算器来计算并返回它的值。整数除法仅保留整数部分。
示例与说明
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/basic-calculator-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
二,解题思路
这个题解描述的非常详细,还包含了其他关于表达式求解题目的扩展计算,关键部分如下方截图:
针对本题(表达式中所有整数为非负整数,也就是第一个数不可能为负。而且只有两种优先级的操作符,没有左右括号)有一个简单的方法:记录前一个操作符preOperation(该方法在上述题解的评论区中)
比如2+3*4/2-1,为了方便处理结尾,可以在表达式后加0即2+3*4/2-1+0,每遇到一个操作符时可以获得以下信息:当前操作符,前一个操作符preOperation,前一个操作符两边的操作数(一个在栈顶,一个是当前数字)。根据操作符进行不同的处理
for (char c : s.toCharArray()) {
if (c == ' ') continue;
if (Character.isDigit(c)) {
num = num * 10 + (c-'0');
} else {
switch (preOperation) {
case '+': stack.push(num); break;
case '-': stack.push(-1 * num); break;
case '*': stack.push(stack.pop() * num); break;
case '/': stack.push(stack.pop() / num); break;
default:
}
preOperation = c;
num = 0;
}
}
循环结束,操作数栈内只剩下需要加减的元素,全部相加即可
return stack.stream().mapToInt(Integer::intValue).sum();
三,AC代码
Java
详细版
class Solution {
// 使用 map 维护一个运算符优先级
// 这里的优先级划分按照「数学」进行划分即可
Map<Character, Integer> map = new HashMap<>(){{
put('-', 1);
put('+', 1);
put('*', 2);
put('/', 2);
put('%', 2);
put('^', 3);
}};
public int calculate(String s) {
// 将所有的空格去掉
s = s.replaceAll(" ", "");
char[] cs = s.toCharArray();
int n = s.length();
// 存放所有的数字
Deque<Integer> nums = new ArrayDeque<>();
// 为了防止第一个数为负数,先往 nums 加个 0
nums.addLast(0);
// 存放所有「非数字以外」的操作
Deque<Character> ops = new ArrayDeque<>();
for (int i = 0; i < n; i++) {
char c = cs[i];
if (c == '(') {
ops.addLast(c);
} else if (c == ')') {
// 计算到最近一个左括号为止
while (!ops.isEmpty()) {
if (ops.peekLast() != '(') {
calc(nums, ops);
} else {
ops.pollLast();
break;
}
}
} else {
if (isNumber(c)) {
int u = 0;
int j = i;
// 将从 i 位置开始后面的连续数字整体取出,加入 nums
while (j < n && isNumber(cs[j])) u = u * 10 + (cs[j++] - '0');
nums.addLast(u);
i = j - 1;
} else {
if (i > 0 && (cs[i - 1] == '(' || cs[i - 1] == '+' || cs[i - 1] == '-')) {
nums.addLast(0);
}
// 有一个新操作要入栈时,先把栈内可以算的都算了
// 只有满足「栈内运算符」比「当前运算符」优先级高/同等,才进行运算
while (!ops.isEmpty() && ops.peekLast() != '(') {
char prev = ops.peekLast();
if (map.get(prev) >= map.get(c)) {
calc(nums, ops);
} else {
break;
}
}
ops.addLast(c);
}
}
}
// 将剩余的计算完
while (!ops.isEmpty()) calc(nums, ops);
return nums.peekLast();
}
void calc(Deque<Integer> nums, Deque<Character> ops) {
if (nums.isEmpty() || nums.size() < 2) return;
if (ops.isEmpty()) return;
int b = nums.pollLast(), a = nums.pollLast();
char op = ops.pollLast();
int ans = 0;
if (op == '+') ans = a + b;
else if (op == '-') ans = a - b;
else if (op == '*') ans = a * b;
else if (op == '/') ans = a / b;
else if (op == '^') ans = (int)Math.pow(a, b);
else if (op == '%') ans = a % b;
nums.addLast(ans);
}
boolean isNumber(char c) {
return Character.isDigit(c);
}
}
作者:AC_OIer
链接:https://leetcode-cn.com/problems/basic-calculator-ii/solution/shi-yong-shuang-zhan-jie-jue-jiu-ji-biao-c65k/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
简单版
public int calculate(String s) {
s = s + "+0";
char preOperation = '+';
int num = 0;
Stack<Integer> stack = new Stack<>();
for (char c : s.toCharArray()) {
if (c == ' ') continue;
if (Character.isDigit(c)) {
num = num * 10 + (c-'0');
} else {
switch (preOperation) {
case '+': stack.push(num); break;
case '-': stack.push(-1 * num); break;
case '*': stack.push(stack.pop() * num); break;
case '/': stack.push(stack.pop() / num); break;
default:
}
preOperation = c;
num = 0;
}
}
return stack.stream().mapToInt(Integer::intValue).sum();
}
四,解题过程
第一博
时间隔得久了,原以为能轻松拿下的,结果一晚上都卡在这里了。。。
还写下了下面屎山一样的代码,最重要的是结果还是错的。。。
错误原因是算法思路有问题:遇到低优先级运算符时,将操作数栈内的高优先级操作符逐个弹出计算,最后栈内只剩低优先级操作符,重新遍历一遍操作数栈计算即可。
这样就会有一种倒着计算的感觉,比如1+2*3/2*3-2,在计算-2时,依次计算2*3=6、3/6=0、2*0=0.(从根本逻辑上就出错了)
代码贴出来以示警告o( ̄┰ ̄*)ゞ
// 这是错误的代码!!!
class Solution {
public int calculate(String s) {
int ans = 0;
StringBuilder tem = new StringBuilder();
Deque<Integer> num = new LinkedList<>();
Deque<Character> operator = new LinkedList<>();
String S = s.concat("@");// 设置虚拟操作符 优先级和+ -相同
// System.out.println(S);
for (int i = 0; i < S.length(); i++ ) {
if (S.charAt(i) == ' ') continue;
// 这种方法拼接效率太低了 建议使用num = num * 10 + (c-'0')来获取操作符
if (S.charAt(i) >= '0' && S.charAt(i) <= '9') {
tem.append(Character.toString(S.charAt(i)));
continue;
}
num.push(Integer.parseInt(tem.toString()));
// System.out.println(num);
// System.out.println(operator);
tem = new StringBuilder();
switch(S.charAt(i)) {
case '+':
case '-':
case '@':
while (!operator.isEmpty() && (operator.peek() == '*' || operator.peek() == '/')) {
// 注意顺序:先弹出的是操作数b 然后才是a
int b = num.pop();
int a = num.pop();
if (operator.peek() == '*') num.push(a * b);
else if (operator.peek() == '/') num.push(a / b);
operator.pop();
}
operator.push(S.charAt(i));
break;
case '*':
case '/':
operator.push(S.charAt(i));
break;
}
}
// System.out.println(num);
// System.out.println(operator);
operator.pop();// 弹出设置的虚拟操作符
// System.out.println(num);
// System.out.println(operator);
while (!operator.isEmpty()) {
System.out.println(num);
System.out.println(operator);
// 注意顺序:从栈底向栈顶逐个进行运算(比如1-2+3,正确结果应该为2,但仍按照栈的操作来就是2+3=5,然后1-5=-4)
// 先弹出的是操作数b 然后才是a
int a = num.removeLast();
int b = num.removeLast();
// System.out.println(a);
// System.out.println(b);
if (operator.getLast() == '+') num.addLast(a + b);
else if (operator.getLast() == '-') num.addLast(a - b);
// else if (operator.peek() == '*') num.push(a * b);
// else if (operator.peek() == '/') num.push(a / b);
operator.removeLast();
}
return num.peek();
}
}
第二搏
参考大佬的解法,这一题果然不简单。。。(简单版代码执行结果