给你一个字符串表达式 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位 整数
这道题可以用两种做法:
第一种朴素的做法是:
将字符串转化为 后缀表达式, 然后利用栈进行求值。
这种做法的注意点:
1.首先把字符串中间多余的空格得处理了。String.replaceAll处理
2.可能会遇到负数,以及 类似于 -(2 + 3)这种情况 或者 a + (-b)这种情况 或者 a - ( + b + b) 具体处理做法加0 将离奇古怪的表达都转化为正常表达,将对负数的运算转化为对 0 - 正数的运算。 像 -(2+3)的话 得要在前面+0 变成 0-(2+3) a +(-b)变为 a + (0 - b)等。
对字符串处理完,就要将字符串转为后缀表达式,具体用辅助栈进行。辅助栈用来接受操作符。在转换的时候要用到操作符之间的优先级,所以 为了快速查找优先级,在map中定义好 操作数的优先级。
这里用ArrayList 当作结果集。
具体转后缀表达式的做法:
1. 如果当前遇到操作数,直接入结果集。
2.如果遇到 "("直接入栈。
3.如果遇到 "+","-","*","/"中的任何一个(*,/ 的优先级大于加减, 乘除优先级相同,加减优先级相同)首先检查栈顶元素优先级是不是大于等于 当前操作符优先级,如果大于等于当前操作数则出栈并且进入结果集,直到栈空或遇到小于此优先级的操作符或遇到“(”,然后加入当前操作符 , 左括号其实是优先级最大的运算符,但是它是个意外,规定不准弹出。
4.如果遇到“)”,则持续弹出栈中操作符,入结果集,知道遇到左括号,然后把左括号弹出丢掉,注意后缀表达式不允许有括号,所以括号要丢掉。
上述操作是中缀转后缀表达式的做法。
然后利用栈进行后缀表达式求职,具体代码如下:
class Solution {
public int calculate(String s) {
s = s.replaceAll(" ", "");
StringBuilder sb = new StringBuilder();
if(s.charAt(0) == '-'){
sb.append(0);
}
sb.append(s.charAt(0));
for(int i = 1; i < s.length(); i++){
char pre = s.charAt(i - 1);
if((pre == '(' || pre == '+' || pre == '-') && (s.charAt(i) == '+' || s.charAt(i) == '-')){
sb.append(0);
}
sb.append(s.charAt(i));
}
ArrayList<String> list = conver(sb.toString());
Stack<Integer> stack = new Stack();
for(String e : list){
if(e.equals("+") || e.equals("-")){
int a = stack.pop();
int b = stack.pop();
int result = 0;
if(e.equals("+")) result = a + b;
else result = b - a;
stack.push(result);
}else{
stack.push(Integer.parseInt(e));
}
}
return stack.peek();
}
public ArrayList<String> conver(String s){
Stack<String> stack = new Stack();
ArrayList<String> list = new ArrayList();
for(int i = 0; i < s.length(); i++){
char cur = s.charAt(i);
if(cur >= '0' && cur <= '9'){
int end = i + 1;
String num = "" + s.charAt(i);
while(end < s.length() && s.charAt(end) >= '0' && s.charAt(end) <= '9'){
num = num + s.charAt(end);
end++;
}
i = end - 1;
list.add(num);
}else if(cur == '('){
stack.push(cur+"");
}else if(cur == ')'){
while(!stack.peek().equals("(")){
list.add(stack.pop());
}
stack.pop();
}else{
while(!stack.isEmpty() && !stack.peek().equals("(")){
list.add(stack.pop());
}
stack.push(cur+"");
}
}
while(!stack.isEmpty()){
list.add(stack.pop());
}
return list;
}
}
这种操作比较简单:比较容易理解。
方法2:利用双栈,进行中缀表达式求值。
一个栈入操作数,一个栈入操作符。
1. 数据处理, 去掉空格。
2.为了防止出现第一个是负号 即 -(2 + 3)在操作数栈先入一个0.
3.然后一边遍历,一边求值:
如果当前 是 左括号,直接入栈,如果是数字,则一直读到不是数字的地方停止,将数入操作数栈。
如果是操作符,则先将这个操作符前面 前面的数都算好(算到直到栈为空,或者遇到左括号)并结果入栈,然后判断操作符左边 是不是左括号如果是则在操作数栈入0(防止(-a)(+b)这种情况发生,操作数栈除了算出来的负数,不加任何原字符串的负数。
如果是右括号,则不断出操作数栈和操作符栈,进行计算并且结果入栈。
循环结束后,还要将操作符栈和操作数出栈计算结果。操作数栈道最后一个就是所求答案。
代码如下:
class Solution {
public int calculate(String s) {
// 存放所有的数字
Deque<Integer> nums = new ArrayDeque<>();
// 为了防止第一个数为负数,先往 nums 加个 0
nums.addLast(0);
// 将所有的空格去掉
s = s.replaceAll(" ", "");
// 存放所有的操作,包括 +/-
Deque<Character> ops = new ArrayDeque<>();
int n = s.length();
char[] cs = s.toCharArray();
for (int i = 0; i < n; i++) {
char c = cs[i];
if (c == '(') {
ops.addLast(c);
} else if (c == ')') {
// 计算到最近一个左括号为止
while (!ops.isEmpty()) {
char op = ops.peekLast();
if (op != '(') {
calc(nums, ops);
} else {
ops.pollLast();
break;
}
}
} else {
if (isNum(c)) {
int u = 0;
int j = i;
// 将从 i 位置开始后面的连续数字整体取出,加入 nums
while (j < n && isNum(cs[j])) u = u * 10 + (int)(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() != '(') calc(nums, ops);
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();
nums.addLast(op == '+' ? a + b : a - b);
}
boolean isNum(char c) {
return Character.isDigit(c);
}
}
作者:AC_OIer
链接:https://leetcode-cn.com/problems/basic-calculator/solution/shuang-zhan-jie-jue-tong-yong-biao-da-sh-olym/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。