一、题目
输入一个字符串格式的算术表达式,对其进行求值。
字符串中的有效字符包括[‘0’-‘9’],‘+’,‘-’, ‘*’,‘/’ ,‘(’, ‘)’,‘[’, ‘]’,‘{’ ,‘}’。
算术表达式的有效性由调用者保证。
接口如下:
/* 功能:四则运算 * 输入:strExpression:字符串格式的算术表达式,如: "3+2*{1+2*[-4/(8-6)+7]}" * 返回:算术表达式的计算结果 */ public static int calculate(String strExpression) { /* 请实现*/ return 0; }
二、总体思路
1、先对字符串格式的算术表达式进行分割,将操作数、操作符和括号分割开来,并存储在ArrayList中。
2、对上述分割开来的算术表达式ArrayList进行转换,转换成后缀表达式。
3、对后缀表达式进行求值。
三、分割算术表达式
//'-' may be minus operator or negative sign private static boolean isDigital(char c, String infixExpression, int i) { //don't need to think about exception //may be a part of digital if(c == '-') { if(i == 0) return true;//For example: -14*24, a part of digital(-14) char prev = infixExpression.charAt(i-1); if('0' <= prev && prev <= '9') return false;//For example: 34-56, not a part of digital else if(prev == ')') return false;//For example: 3-10+(0+(10+5+3)-10), not a part of digital else return true;//For example: 45*12/-12, a part of digital(-12); (-45/12)+2, a part of digital(-45) } return '0' <= c && c <= '9'; }
private static ArrayList<String> splitExpression(String infixExpression) { ArrayList<String> infix = new ArrayList<String>(); infixExpression = infixExpression.replace('{', '('); infixExpression = infixExpression.replace('}', ')'); infixExpression = infixExpression.replace('[', '('); infixExpression = infixExpression.replace(']', ')'); StringBuilder operand = new StringBuilder(); for(int i = 0; i < infixExpression.length(); i++) { char c = infixExpression.charAt(i); if(!isDigital(c, infixExpression, i)) { if(operand.length() != 0) { infix.add(operand.toString());//last operand operand = new StringBuilder();//empty } infix.add(c + "");//operator } else { operand.append(c); if(i == infixExpression.length() - 1){ infix.add(operand.toString());//the last operand in the end } } } return infix; }
这里需要重点处理‘-’这个符号,有可能为减号或者为负号。
思路如下:
1、首先将[]{}等括号替换成(),方便统一处理。
2、遍历字符串表达式,如果当前字符为数字,那么先缓存到操作数operand字符串中。如果是最后一个字符,将operand添加到输出中。
3、如果当前字符为非数字,那么先添加operand到输出中(非空),再将当前操作符添加到输出中。
判断是否为数字的思路(需要特别处理‘-’符号):
1、当前符号为‘-’时:
如果前一个字符是‘0’到‘9’时,其为非数字。
如果前一个字符是‘)’时,其为非数字。
如果前一个字符时‘*’或‘/’或‘+’或‘-’或‘(’时,其为数字的一部分。
2、当前符号不为‘-’时:
只需判断是否为‘0’到‘9’,若是则为数字,若不是,则为非数字。
四、将中缀转换成后缀表达式
private static boolean isOperand(String item) { //don't need to handle the situation when item is null if(item.length() > 1) { return true;//must be operand } else {//don't need to think about the situation that length lower than 1 if('0' <= item.charAt(0) && item.charAt(0) <= '9') { return true; } else { return false; } } }
//A < B? private static boolean isALowerB(String operatorA, String operatorB) { if(operatorB.equals("(")) {//highest priority return true;//dont need to handle the situation that operatorA is "(" } else if(operatorB.equals("*") || operatorB.equals("/")) { if(operatorA.equals("+") || operatorA.equals("-")) { return true;//A is lower } else { return false;//the same } } else { return false;//operatorB is already lowest } }
private static ArrayList<String> convertInfix2Postfix(ArrayList<String> infix) { ArrayList<String> postfix = new ArrayList<String>(); Stack<String> st = new Stack<String>(); while(!infix.isEmpty()) { String s = infix.remove(0); if(isOperand(s)) { postfix.add(s);//operand, placed immediately } else { if(s.equals(")")) {//right parenthesis, pop the stack until encounter a (correspoding) left parenthesis while(!st.empty() && !st.peek().equals("(")) {//don't need to think about the exception postfix.add(st.pop()); } st.pop();//pop left parenthesis, discard it. } else { //pop until find an entry of lower priority while(!st.isEmpty() ) { if(isALowerB(st.peek(), s)) { break; } else { if(st.peek().equals("(")) break;//never remove a '(' from the stack except when processing a ')' postfix.add(st.pop()); } } st.push(s);//push new entry } } }
while(!st.empty()) { postfix.add(st.pop()); }
return postfix; }
思路:
1、如果当前字符串是操作数,直接将这个操作数添加到输出中。
2、如果当前字符串是操作符,那么需要进一步处理。需要用一个stack来缓存。
3、如果当前操作符是“)”,那么需要将stack中所有操作符依次添加到输出中直到遇到相匹配的“(”。“(”直接丢弃。
4、剩余的情况,需要先将stack中的操作符添加到输出,直到遇到更低优先级的操作符(“(”例外,不输出,除非遇到相匹配的“)”)或者栈为空。
5、再将新的操作符添加到stack中。
6、处理完所有字符串之后,需要将stack中剩余的所有操作符依次添加到输出中。
五、对后缀表达式进行求值
private static int calculatePostfix(ArrayList<String> postfix) { Stack<Integer> st = new Stack<Integer>(); while(!postfix.isEmpty()) { String s = postfix.remove(0); if(isOperand(s)) { st.push(Integer.parseInt(s)); } else { char operator = s.charAt(0); int temp = 0; switch (operator) { case '+': st.push(st.pop() + st.pop()); break; case '-': temp = st.pop(); st.push(st.pop() - temp); break; case '*': st.push(st.pop() * st.pop()); break; case '/': temp = st.pop(); st.push(st.pop() / temp); break; default: break; } } } return st.pop(); }
思路:
1、如果是操作数,压栈。
2、如果是操作符,弹出两个操作数,进行相应的计算,再将结果压栈。
重复上述操作,直到处理完表达式,此时栈中应该刚好剩余一个操作数,即为最终结果。
六、实现和测试
package com.qiusongde; import java.util.*; public class Test { public static void main(String[] args) { Scanner sc = new Scanner(System.in); while(sc.hasNext()) { String input = sc.next(); System.out.println(calculate(input)); } sc.close(); } public static int calculate(String infixExpression) { ArrayList<String> list = new ArrayList<>(); list = splitExpression(infixExpression); list = convertInfix2Postfix(list); int result = calculatePostfix(list); return result; } //'-' may be minus operator or negative sign private static boolean isDigital(char c, String infixExpression, int i) { //don't need to think about exception //may be a part of digital if(c == '-') { if(i == 0) return true;//For example: -14*24, a part of digital(-14) char prev = infixExpression.charAt(i-1); if('0' <= prev && prev <= '9') return false;//For example: 34-56, not a part of digital else if(prev == ')') return false;//For example: 3-10+(0+(10+5+3)-10), not a part of digital else return true;//For example: 45*12/-12, a part of digital(-12); (-45/12)+2, a part of digital(-45) } return '0' <= c && c <= '9'; } private static ArrayList<String> splitExpression(String infixExpression) { ArrayList<String> infix = new ArrayList<String>(); infixExpression = infixExpression.replace('{', '('); infixExpression = infixExpression.replace('}', ')'); infixExpression = infixExpression.replace('[', '('); infixExpression = infixExpression.replace(']', ')'); StringBuilder operand = new StringBuilder(); for(int i = 0; i < infixExpression.length(); i++) { char c = infixExpression.charAt(i); if(!isDigital(c, infixExpression, i)) { if(operand.length() != 0) { infix.add(operand.toString());//last operand operand = new StringBuilder();//empty } infix.add(c + "");//operator } else { operand.append(c); if(i == infixExpression.length() - 1){ infix.add(operand.toString());//the last operand in the end } } } return infix; } private static boolean isOperand(String item) { //don't need to handle the situation when item is null if(item.length() > 1) { return true;//must be operand } else {//don't need to think about the situation that length lower than 1 if('0' <= item.charAt(0) && item.charAt(0) <= '9') { return true; } else { return false; } } } //A < B? private static boolean isALowerB(String operatorA, String operatorB) { if(operatorB.equals("(")) {//highest priority return true;//dont need to handle the situation that operatorA is "(" } else if(operatorB.equals("*") || operatorB.equals("/")) { if(operatorA.equals("+") || operatorA.equals("-")) { return true;//A is lower } else { return false;//the same } } else { return false;//operatorB is already lowest } } private static ArrayList<String> convertInfix2Postfix(ArrayList<String> infix) { ArrayList<String> postfix = new ArrayList<String>(); Stack<String> st = new Stack<String>(); while(!infix.isEmpty()) { String s = infix.remove(0); if(isOperand(s)) { postfix.add(s);//operand, placed immediately } else { if(s.equals(")")) {//right parenthesis, pop the stack until encounter a (correspoding) left parenthesis while(!st.empty() && !st.peek().equals("(")) {//don't need to think about the exception postfix.add(st.pop()); } st.pop();//pop left parenthesis, discard it. } else { //pop until find an entry of lower priority while(!st.isEmpty() ) { if(isALowerB(st.peek(), s)) { break; } else { if(st.peek().equals("(")) break;//never remove a '(' from the stack except when processing a ')' postfix.add(st.pop()); } } st.push(s);//push new entry } } } while(!st.empty()) { postfix.add(st.pop()); } return postfix; } private static int calculatePostfix(ArrayList<String> postfix) { Stack<Integer> st = new Stack<Integer>(); while(!postfix.isEmpty()) { String s = postfix.remove(0); if(isOperand(s)) { st.push(Integer.parseInt(s)); } else { char operator = s.charAt(0); int temp = 0; switch (operator) { case '+': st.push(st.pop() + st.pop()); break; case '-': temp = st.pop(); st.push(st.pop() - temp); break; case '*': st.push(st.pop() * st.pop()); break; case '/': temp = st.pop(); st.push(st.pop() / temp); break; default: break; } } } return st.pop(); } }
测试数据:
3-10+(0+(10+5+3)-10)
输出结果:
1