227. 基本计算器 II
题目
实现一个基本的计算器来计算一个简单的字符串表达式的值。
字符串表达式仅包含非负整数,+, - ,*,/ 四种运算符和空格 。 整数除法仅保留整数部分。
示例 1:
输入: “3+2*2”
输出: 7
示例 2:
输入: " 3/2 "
输出: 1
示例 3:
输入: " 3+5 / 2 "
输出: 5
解法
栈
利用栈将四则运算化简成加法运算
此算法的思路很简单,先把乘除法的值计算出来,最终将所有的运算简化成只有加法。
- 先跳过空格
- 出现了数字则记录整个数字是多少,然后根据之前的运算符决定下一步:
- 如果是加号’+’,说明前面的运算独立于以后的运算,可以将结果暂时放入栈;
- 如果是减号’-’,可以看成-1 * tempNum,然后将-tempNum入栈;
- 如果是乘号’*‘或者除号’/’,由于前面的运算独立于此,可以先计算lastNum和tempNum积,然后结果入栈。
- 最后将栈中的所有元素相加就是答案。
public int calculate(String s) {
Deque<Integer> stack = new LinkedList<>();
char[] arr = s.toCharArray();
char lastOps = '+';
for (int i = 0; i < s.length(); i++) {
// 跳过空格
if (arr[i] == ' ') {
continue;
}
if (Character.isDigit(arr[i])) {
int tempNum = arr[i] - '0';
// 如果是数字则继续向后遍历计算出数字
while (++i < s.length() && Character.isDigit(arr[i])) {
tempNum = tempNum * 10 + arr[i] - '0';
}
// 最后多了一个+1的操作,需要-1
i--;
// 判断运算符的类型:+则直接入栈、-将相反数入栈、*/则将栈顶元素出栈并运算
if (lastOps == '+') {
stack.push(tempNum);
} else if (lastOps == '-') {
stack.push(-tempNum);
} else if (lastOps == '*') {
stack.push(stack.pop() * tempNum);
} else {
stack.push(stack.pop() / tempNum);
}
// 如果是运算符则记录运算符
} else {
lastOps = arr[i];
}
}
// 计算其中每个运算的值
int num = 0;
for (int i : stack) {
num += i;
}
return num;
224. 基本计算器
题目
实现一个基本的计算器来计算一个简单的字符串表达式 s 的值。
示例 1:
输入:s = “1 + 1”
输出:2
示例 2:
输入:s = " 2-1 + 2 "
输出:3
示例 3:
输入:s = “(1+(4+5+2)-3)+(6+8)”
输出:23
s 由数字、’+’、’-’、’(’、’)’、和 ’ ’ 组成
解法
本题与上面那题的区别在于本题包含括号。
方法一:逆向读取字符串并使用栈
因为表达式包含 嵌套括号,可以使用「栈」暂存带括号的中间变量和结果。
通过具体例子研究一般规律:
示例 3:
输入:s = “(1+(4+5+2)-3)+(6+8)”
输出:23
- 逆序读取代字符串,遇到右括号 ) 表示须要暂存接下来的数字和符号,遇到左括号 ( 表示可以结算在遇到 离当前最近 的右括号 ) 之间缓存的数值,由于是逆序读取,这个过程符合 后进先出 的规律,因此缓存的数据结构须要使用「栈」;
- 如果参与计算的数由多个字符组成,例如:字符串 “123”,表示数值 123,逆序读取的时候可以这样转换:3 + 20 + 100,可以设置一个变量 n ,从 1 开始,接下来变为 10、100,这样 3 + 20 + 100 = 3 * 1 + 2 * 10 + 1 * 100;
- 如果遇到的字符不是数字而是「运算符或者括号」,先把数字存入栈,然后
-
- 如果是运算符,+ 存入 1,- 存入 -1;
-
- 如果是括号,左括号结算(因为当前括号里的内容可以计算出来,计算完成以后成为另一个括号里的数参与计算),右括号缓存起来
- 最后栈中还有元素,还应该做一次计算。
class Solution {
private int RIGHT_BRACKET = ')';
public int calculate(String s) {
// 去除左右空格
s = s.trim();
// 如果以负号开始则在最前面补0
if (s.charAt(0) == '-') {
s = "0" + s;
}
int len = s.length();
char[] chars = s.toCharArray();
// 只使用一个栈(保存操作数和操作符)
Deque<Integer> stack = new LinkedList<>();
// 累计的位数,转换 123,从右向左,底数从 1 开始,依次为 10、100、……
int n = 1;
// 累积的操作数
int operand = 0;
// 从后往前遍历
for (int i = len - 1; i >= 0; i--) {
char c = chars[i];
// 跳过空格
if (c == ' ') {
continue;
}
// 累积计算操作符
if (Character.isDigit(c)) {
operand = n * (c - '0') + operand;
n *= 10;
continue;
}
// 将之前的操作数保存到栈,并且初始化 operand 和 n
if (n != 1) {
stack.push(operand);
n = 1;
operand = 0;
}
// 加减操作都转化为加法
if (c == '+') {
stack.push(1);
continue;
}
if (c == '-') {
stack.push(-1);
continue;
}
// 左括号结算(因为当前括号里的内容可以计算出来,计算完成以后成为另一个括号里的数参与计算)
// 右括号缓存起来
if (c == '(') {
int res = evalExpr(stack);
stack.pop();
stack.push(res);
} else {
stack.push(RIGHT_BRACKET);
}
}
// 最后的数字加入栈中
if (n != 1) {
stack.push(operand);
}
// 最后计算一下整体的结果
return evalExpr(stack);
}
public int evalExpr(Deque<Integer> stack) {
int res = 0;
if (!stack.isEmpty()) {
res = stack.pop();
}
// 每次取出一个操作符和运算的数进行计算
while (stack.size() > 1 && stack.peek() != RIGHT_BRACKET) {
res += stack.pop() * stack.pop();
}
return res;
}
}