Algo156 算法训练 表达式计算
题目如下:
这道题也挺经典的,虽然大部分人都用后缀表达式来写,但我打算用我脑子里第一时间想到的方法来写,java评测在100ms-200ms左右,效率虽然不高,但勉强能用。而且这算法没那么高大上,想想就出来了。
第一步就是简单化,首先我就想到怎么把括号给搞掉,这个括号毕竟不算运算符,挺麻烦的。最后我打算把每个括号内的表达式提取出来,算完了再用结果把原来的括号+括号内的表达式都替换掉。比如1-2+3×(4-5),会先把4-5提出来,得到结果为-1,原来表达式变成1-2+3×-1,在把这个表达式解出来即可,所以核心就是解没有括号的表达式,只有加减乘除还是简单的。
第二步就是只有加减乘除的表达式怎么解决啊,考虑到运算符的优先级不同,只能用到栈了。
代码如下,除了核心的运算外,我的注释足以说明 核心用下列来说明
比如一个 1-2×3+4×5/1
1.首先会把1放到数栈里,然后判断其后的符号是-,也直接存到符号栈里 这时数栈为1 符号栈为-
2.第二次循环,会把2存到数栈,然后发现其后的符号是×,这时不会把×存到栈里,而是直接运算调,这是会把2从栈中取出,然后在获取下一个数3,运算一下得到6,把这个6再存到栈中,这时候数栈为1,6。符号栈为-
3.开始第三轮循环,因为我有判断,之前进行的是乘或者除的话,这轮就不会存数字,因为3已经被上一轮的乘用掉了,所以这轮直接取符号,发现符号是一个+,这时我会看符号栈是否为空,不为空就取出来运算,因为在符号栈里的只可能是-和+,所以这没优先级,把之前存的运算掉即可,但运算后的结果和这轮本要存的符号还是要存到栈里,这轮结束,数栈为-5(1减6所得),符号栈为+
4.上述三轮结束其实表达式已经变为-5+4×5/1了,剩下的计算过程类似,可以自己推导下。
package algo;
import java.util.*;
import java.util.stream.Collectors;
/**
* @Description: 算法训练 表达式计算
* @ClassName: Algo156
* @author: fan.yang
* @date: 2020/07/18 10:36
*/
public class Algo156 {
/**
* 这个计算函数只处理不包括括号的表达式
*/
public static int calculation(String str){
//数栈
Stack<Integer> stack1 = new Stack<>();
//符号栈
Stack<Character> stack2 = new Stack<>();
//这个过程就算提取数字和符号的
char[] array = str.toCharArray();
List<Character> symbol = new ArrayList<>();
for(int i = 1;i < array.length;i++){
if(!Character.isDigit(array[i]) && Character.isDigit(array[i - 1])){
symbol.add(array[i]);
array[i] = ' ';
}
}
List<Integer> num = Arrays.asList(String.valueOf(array).split(" ")).stream()
.map(e -> Integer.valueOf(e)).collect(Collectors.toList());
//下面就算运用双栈来计算了 这个还是画个图比较好
boolean flag = true;
for(int i = 0;i < num.size();i++){
if(flag){
stack1.push(num.get(i));
}
if(i == symbol.size()){
break;
}
if(symbol.get(i) == '-' || symbol.get(i) == '+'){
flag = true;
if(!stack2.empty()){
int a = stack1.pop();
int b = stack1.pop();
stack1.push(stack2.pop() == '-' ? b - a : b + a);
}
stack2.push(symbol.get(i));
}else{
flag = false;
int a = stack1.pop();
int b = num.get(i + 1);
if(symbol.get(i) == '*'){
stack1.push(a * b);
}else{
stack1.push(a / b);
}
}
}
//因为上述循环可能会留下一组 加 减操作 所以这里只需要判断下符号栈了还有没有
//符号栈不为空就最后操作一下
if(stack2.empty()){
return stack1.pop();
}else{
int a = stack1.pop();
int b = stack1.pop();
return stack2.pop() == '-' ? b - a : b + a;
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
StringBuffer sb = new StringBuffer(scanner.next());
//这个indexOf就是判断表达式还有没有括号
while(sb.indexOf("(") != -1){
//这里为啥要用lastIndexOf 考虑到会有多层括号,当然要从里面那层开始
//就算都是同一层的括号,我从哪里开始都一样的
int left = sb.lastIndexOf("(");
//获取与left括号对应的右括号
int right = sb.indexOf(")" , left);
//截取上述括号之间的表达式
String str = sb.substring(left + 1 , right);
int num = calculation(str);
//得到结果后覆盖
sb.replace(left , right + 1 , num + "");
}
//去除掉括号 就只剩下基本的加减乘除表达式了 最后调一次计算函数即可
System.out.println(calculation(sb.toString()));
}
}