逆波兰算法求解算术表达式
牛客题目:请写一个整数计算器,支持加减乘三种运算和括号。
题目给出的算术表达式主要包括三部分:操作数、括号、运算符
一般的算术表达式都是中缀表达式,不易于编程计算。所以我们可以利用逆波兰算法将算术表达式的中缀形式转为更易于计算的后缀形式。
转换为后缀表达式的过程需要借助两个额外空间,栈S和数组列表L,其中栈用于存储左括号和运算符,数组列表用于存储后缀表达式。
整个步骤是:
遍历算术表达式:
1、如果遇到了操作数,那么此时需要考虑一个问题:那就是当前这个操作数是单位数还是多位数的一部分,如果是单位数,那么直接加入到数组列表L中;如果是多位数,那么需要利用while循环,将这个多位数连接起来,形成一个整体,然后放入数组列表
2、如果遇到了分界符,即括号,那么判断它如果是左括号“(”,则直接入栈;如果是右括号“)”,则说明已有一对括号配对,那么直接丢弃这个右括号,然后对栈进行出栈操作,将出栈的元素加入到L中,然后一直循环出栈,直到遇到左括号,则出栈结束,并将出栈的左括号丢弃。
3、如果遇到了运算符op1,此时观察栈顶元素op2
(1)如果栈为空,则直接将op1入栈;
(2)如果op2不是运算符,则直接将op1入栈;
(3)如果op2是运算符,则比较op1和op2的优先级:如果op1优先级大于op2,则直接将op1入栈;否则,将op2出栈,并加入到L中,然后如果新的栈顶依旧是运算符的话,则继续进行优先级的判断;
其中,第(2)(3)步骤可以通过一个while循环解决,while循环的条件是:栈非空&&栈顶元素是运算符&&栈顶元素优先级大于op1的优先级,这样做的目的是保证如果栈中有连续相邻的运算符,则上边的运算符的优先级要大于下边的运算符的优先级。
4、最后,如果栈中还有元素的话,则全部追加到数组列表中。
以上步骤就是中缀表达式转为后缀表达式的过程,然后就是利用得到的后缀表达式计算结果了,具体过程是:
1、仍需借助一个栈,用于存储操作数和中间结果
2、对后缀表达式进行遍历
(1)如果遇到的是操作数,则进栈;
(2)如果遇到了运算符,则连续出栈两个元素,其中先出栈的是运算符右边的元素,后出栈的是运算符左边的元素,然后按照运算符对两个数进行计算,得出的中间结果再次放入栈中,
3、遍历完成之后,栈顶元素就是最终的计算结果。
private HashMap<Character,Integer> pri = new HashMap<>();
public int solve (String s) {
// write code here
if (s == null){
return 0;
}
pri.put('+',0);
pri.put('-',0);
pri.put('*',1);
List<String> suffixs = getSuffix(s);
System.out.println(suffixs);
Stack<Integer> stack = new Stack<>();
int res = 0;
for (String suffix : suffixs) {
if (suffix.equals("+") || suffix.equals("-") || suffix.equals("*")){
Integer num1 = stack.pop();
Integer num2 = stack.pop();
switch (suffix){
case "+":stack.push(num2 + num1);
break;
case "-":stack.push(num2 - num1);
break;
case "*":stack.push(num2 * num1);
break;
}
}else {
stack.push(Integer.valueOf(suffix));
}
}
return stack.pop();
}
private List<String> getSuffix(String s){
Stack<Character> stack = new Stack<>();
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < s.length();) {
int old_i = i;
char curChar = s.charAt(i);
// 1、分隔符
if (curChar == '('){
stack.push(curChar);
}else if (curChar == ')'){
while (!stack.isEmpty()){
Character ch = stack.pop();
if (ch == '('){
break;
}
list.add(ch.toString());
}
// 2、运算符
}else if (curChar == '+' || curChar == '-' || curChar =='*'){
if (stack.isEmpty()){
stack.push(curChar);
}else {
while (!stack.isEmpty() &&(stack.peek() == '+' || stack.peek() == '-' || stack.peek() =='*')&&pri.get(curChar) <= pri.get(stack.peek())){
Character pop = stack.pop();
list.add(pop.toString());
}
stack.push(curChar);
}
// 3、操作数
}else {
StringBuilder cur = new StringBuilder();
while (i < s.length() && s.charAt(i) >= '0' && s.charAt(i) <= '9'){
cur.append(s.charAt(i));
i++;
}
list.add(cur.toString());
}
if (old_i == i){
i ++;
}
}
while (!stack.isEmpty()){
list.add(stack.pop().toString());
}
return list;
}