实现一个基本的计算器来计算一个简单的字符串表达式的值。
字符串表达式可以包含左括号 ( ,右括号 ),加号 + ,减号 -,非负整数和空格 。
原题链接:https://leetcode-cn.com/problems/basic-calculator/
示例 1:
输入: “1 + 1”
输出: 2
示例 2:
输入: " 2-1 + 2 "
输出: 3
示例 3:
输入: “(1+(4+5+2)-3)+(6+8)”
输出: 23
说明:
你可以假设所给定的表达式都是有效的。
请不要使用内置的库函数 eval。
思路:中缀表达式转后缀表达式+后缀表达式计算结果
中缀表达式转后缀表达式
- 从左到右遍历中缀表达式,如果是数字,就添加到后缀表达式中
- 如果是符号,就判断运算符栈是否为空;如果为空,就直接添加到运算符栈中
如果不为空,就判断其与运算符栈顶元素的优先级,是右括号或者低于栈顶运算符优先级,就依次出栈,并将当前运算符入栈; - 重复上一步操作,直至中缀表达式遍历完成
- 判断栈中是否为空。如果不为空,就依次取出栈顶元素放到后缀表达式中,直到栈中为空
后缀表达式计算结果
- 准备一个数字栈。从左到右依次遍历后缀表达式,如果是数字,就放入栈中;
- 如果是运算符,就从栈中弹出两个数字,第一个取出的数字为右运算数,第二个取出的数字为左运算数,进行运算,并将结果压入栈中
- 重复操作,直至后缀表达式遍历完成,栈中的数字就是表达式的结果
时间复杂度和空间复杂度均为O(N),其中N为字符串的长度。
代码:
public int calculate(String s) {
StringBuilder stringBuilder = toSuffix(s);
String[] strings = stringBuilder.toString().split(" ");
int result = getValueOfExpression(strings);
return result;
}
/**
* 获取表达式的值
* @param strings 每一个string字符串为数字或者运算符
* @return 返回表达式的结果
*/
private int getValueOfExpression(String[] strings) {
//数字栈用来保存运算数字
Stack<Integer> numStack = new Stack<>();
for (int i = 0; i < strings.length; i++) {
//当字符是加号时,从栈中弹出两个数字,进行运算并且把结果放回栈中
if (strings[i].charAt(0) == '+'){
int num1 = numStack.pop();
int num2 = numStack.pop();
numStack.push(num2+num1);
}else if(strings[i].charAt(0) == '-'){
//当字符是减号时,从栈中弹出两个数字,进行运算并且把结果放回栈中
int num1 = numStack.pop();
int num2 = numStack.pop();
numStack.push(num2-num1);
}else{
//当字符串是数字时,将数字压入栈中
numStack.push(Integer.parseInt(strings[i]));
}
}
//返回栈中最后的结果
return numStack.pop();
}
/**
* 将中缀表达式转化为后缀表达式
* @param s 中缀表达式
* @return 后缀表达式
*/
private StringBuilder toSuffix(String s){
//用来保存后缀表达式
StringBuilder sb = new StringBuilder();
//保存运算符
Stack<Character> operatorStack = new Stack<>();
for (int i = 0; i < s.length();) {
char temp = s.charAt(i);
//如果是数字,就把数字的连续数字都取出来,并且是空格分隔
if (isNumber(temp)){
int[] result = getNumAndEndIndex(s, i);
sb.append(result[0]);
sb.append(" ");
i = result[1];
}else if(temp == '('){
//如果是左括号,直接压入栈中
operatorStack.push(temp);
i++;
}else if(temp == '+' || temp == '-'){
//如果时+或-,并且栈内不为空
if (!operatorStack.isEmpty()){
while (!operatorStack.isEmpty()){
char item = operatorStack.pop();
//如果是栈顶是左括号,直接推入栈中,并且结束循环
if (item == '('){
operatorStack.push(item);
break;
}else{
//否则就添加到StringBuilder中
sb.append(item);
sb.append(" ");
}
}
}
//并且把符号压入栈中
operatorStack.push(temp);
i++;
}else if(temp == ')'){
//如果是右括号,就一直出栈,直到栈顶为左括号
char item = operatorStack.pop();
while(item != '('){
sb.append(item);
sb.append(" ");
item = operatorStack.pop();
}
i++;
}else{
i++;
}
}
//如果栈内不为空,就一直出栈
while (!operatorStack.isEmpty()){
sb.append(operatorStack.pop());
sb.append(" ");
}
return sb;
}
/**
* 获取字符串的数字以及结束的索引
* @param s 字符串
* @param i 数字开始的索引
* @return 数组:下标0代表数字,下标1代表结束的索引
*/
private int[] getNumAndEndIndex(String s, int i) {
//保存数字以及结束的索引
int num = 0;
int index = i;
for (; index < s.length(); index++) {
if (isNumber(s.charAt(index))){
num = num * 10 + s.charAt(index) - '0';
}else{
break;
}
}
int[] result = new int[2];
result[0] = num;
result[1] = index;
return result;
}
/**
* 判断字符是否为数字
* @param c 字符
* @return 是否为数字
*/
private boolean isNumber(char c){
if(c >= '0' && c <= '9')
return true;
return false;
}