+ - * /
、数字、空格、算法返回运算结果 Integer.parseInt(s);
如果输入的这个算式只包含加减法,而且不存在空格,该如何计算结果呢?我们先手工计算一下算式1-12+3
为例,来说一个很简单的思路
+
,变成+1-12+3
+1
,-12
,+3
,把它转化成数字,然后放到一个栈中 public int calculate(String s) {
Integer.parseInt(s);
Stack<Integer> stack = new Stack<>();
//记录算式中的数字
int num = 0;
//记录num前的符号,初始化为+
char sign = '+';
for(int i = 0;i<s.length();i++){
char c = s.charAt(i);
//如果是数字,就连续读取出来
if(Character.isDigit(c)){
num = 10*num + (c-'0');
}
//如果不是数字,就证明遇到了下一个符号
//之前的数字和符号就要存进栈中
//存栈这个操作 还要在字符串遍历结束之后再来一次
if(!Character.isDigit(c) || i == s.length()-1 ){
switch (sign){
case '+':
stack.push(num);break;
case '-':
stack.push(-num);break;
}
//更新符号为当前符号,数字清零
sign = c;
num=0;
}
}
//将栈中的所有结果进行求和
int res = 0;
while (!stack.isEmpty()){
res += stack.peek();
stack.pop();
}
return res;
}
2-3*4+5
举例,核心思路依然是把字符串分解成符号和数字的组合,比如说上述例子就可以分解为+2
、-3
、*4
、+5
几对,接下来其实思考一下发现其实就是switch中的处理不同而已,因此我们只需要修改switch里面的内容就可以了 //只需要拿出前一个数字做对应的运算即可
case '*':
pre = stack.peek();stack.pop();
stack.push(pre*num);
break;
case '/':
pre = stack.peek();stack.pop();
stack.push(pre/num);
break;
但是这时候有个疑问,这样做是否能够保证乘除法的优先性呢?
乘除法的优先性体现于在:乘除法可以和栈顶的元素结合然后把结果加入栈,而加减法只能把自己加入栈
也可以这么理解,运算顺序问题,当遇到*
和/
的时候都是在case里面就完成的,而+
和-
都是在后面完成的
if((!Character.isDigit(c) && c!=' ') || i == s.length()-1 )
无论括号嵌套多少层,都可以使用一个递归辅助函数来搞定
可以这么说的依据是,计算括号的过程都是完全一样的,具有一个base-case(没有括号的情况),而且如果括号嵌套的话,可以通过一个base-case逐渐状态转移到之后的状态,以字符串3*(4-5/2)-6
举例:
calculate(3*(4-5/2)-6
) = 3*calculate(4-5/2
)-6 = 0
换句话说,括号包含的等式,直接视作为一个数字就可以了
递归开始的条件:遇到(
就开始递归,遇到)
就结束递归
public int calculate(String s) {
return helper(new StringBuilder(s));
}
private int helper(StringBuilder s){
Stack<Integer> stack = new Stack<>();
//记录算式中的数字
int num = 0;
//记录num前的符号,初始化为+
char sign = '+';
while(s.length()>0){
char c = s.charAt(0);
s = s.delete(0, 1);
//如果是数字,就连续读取出来
if(Character.isDigit(c)){
num = 10*num + (c-'0');
}
//如果不是数字,就证明遇到了下一个符号
//之前的数字和符号就要存进栈中
//存栈这个操作 还要在字符串遍历结束之后再来一次
if(c == '('){
num = helper(s);
}
if((!Character.isDigit(c) && c!=' ') || s.length() == 0 ){
int pre = 0;
switch (sign){
case '+':
stack.push(num);break;
case '-':
stack.push(-num);break;
//只需要拿出前一个数字做对应的运算即可
case '*':
pre = stack.peek();stack.pop();
stack.push(pre*num);
break;
case '/':
pre = stack.peek();stack.pop();
stack.push(pre/num);
break;
}
//更新符号为当前符号,数字清零
sign = c;
num=0;
}
if(c == ')'){
break;
}
}
//将栈中的所有结果进行求和
int res = 0;
while (!stack.isEmpty()){
res += stack.peek();
stack.pop();
}
return res;
}
if(c == '('){
num = helper(s);
}
if(c == ')'){
break;
}
(
需要放在switch之前?)
为什么要放在switch之后?(
的时候,我们是将(
作为一个数字来看待的,而数字是需要进入switch进行处理的,因此需要放在switch之前)
的时候,仅根据(!Character.isDigit(c) && c!=' ')
是可以直接在switch之前接上这个条件,但是要注意,当输入的表达式字符串其最后一个字符是)
的时候,直接break掉了,导致之前处理得到的num没有进入stack就会导致漏数。