Java 用栈处理四则运算

一、概念

中缀表达式:即人们常用的算式写法,如8+(9-1)*8+7/2

后缀表达式:运算符放到数字后,如8 9 1 - 8 * + 7 2 /+


二、思路


一共分两步:

1.中缀表达式转后缀表达式

从左到右遍历中缀表达式的每一个数字和运算符。
如果数字就输出(即存入后缀表达式);
如果是右括号,则弹出左括号之前的运算符;
如果优先级低于栈顶运算符,则弹出栈顶运算符,并将当前运算符进栈。
遍历结束后,将栈则剩余运算符弹出。


2.后缀表达式计算结果

从左到右遍历后缀表达式,遇到数字就进栈,遇到符号,就将栈顶的两个数字出栈运算,运算结果进栈,直到获得最终结果。


三、代码

目前只支持整数的四则运算

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 目前只能做简单的四则运算,不支持浮点数和其他符号,不支持多线程
 * 计算步骤:
 * 1.中辍表达式转后缀
 * 2.计算后缀表达式
 */
public class Arithmetic {
    /** 错误提示 **/
    private static final String ERROR_ZERO = "DividedZero";
    
    /** 检测一个字符串是不是数字 **/
    private static Pattern pattern = Pattern.compile("\\d+\\.?\\d?");
    
    
    /** 运算符优先级 **/
    private static Map<String, Integer> priority = new HashMap<>();
    static {
        priority.put("*", 2);
        priority.put("/", 2);
        priority.put("+", 1);
        priority.put("-", 1);
        priority.put("", -1); // 无运算符
    }
    
    /** 替换表达式中的中文括号 **/
    private static Map<String, String> replace = new HashMap<>();
    static {
        replace.put("(", "(");
        replace.put(")", ")");
    }
    
    /** 支持的运算符 **/
    private static String[] operator = {"+", "-", "*", "/"};
    
    private static Stack<String> stack = new Stack<>();
    
    /**
     * -----------测试代码----------------
     */
    public static void main(String[] args) {
        String expression = "8+(9-1)*8+7/2";
        System.out.println(execute(expression));
        
    }
    
    /**
     * 整个计算过程
     * @param expression 传入的表达式
     * @return 计算结果
     */
    public static String execute(String expression) {
        return suffixToValue(inffixToSuffix(trim(expression)));
    }
    
    /**
     * 整理算式,替换掉中文括号等
     * @param expression 算式
     * @return 整理后的算式
     */
    private static String trim(String expression) {
        String result = expression.trim();
        for (Map.Entry<String, String> entry : replace.entrySet()) {
            result = result.replace(entry.getKey(), entry.getValue());
        }
        return result;
    }
    
    /**
     * 将中缀表达式转换成后缀表达式
     * eg:中缀表达式8+(9-1)*8+7/2
     * 后缀表达式8 9 1 - 8 * + 7 2 / +,元素之间之间用空格分隔。
     * 从左到右遍历中缀表达式的每一个数字和运算符
     * 如果数字就输出(即存入后缀表达式)
     * 如果是右括号,则弹出左括号之前的运算符
     * 如果优先级低于栈顶运算符,则弹出栈顶运算符,并将当前运算符进栈
     * 遍历结束后,将栈则剩余运算符弹出。
     * @param expression 中缀表达式
     * @return 后缀表达式
     */
    private static String inffixToSuffix(String expression) {
        stack.clear();
        StringBuilder inffix = new StringBuilder(expression);
        StringBuilder suffix = new StringBuilder();
        String element = ""; // 中缀表达式的数字或者运算符
        String tmp = "";
        
        while (inffix.length() > 0) {
            element = popNextElement(inffix);
            
            if (isNum(element)) { // 是数字则输出
                suffix.append(element).append(" ");
            } else if (")".equals(element)) { // 右括号则将左括号之前的内容全弹出
                tmp = stack.pop();
                while (!"(".equals(tmp)) {
                    suffix.append(tmp).append(" ");
                    tmp = stack.pop();
                }
            } else if ("(".equals(element) || priority.get(element) >= priority.get(getTopOperator())) {
                stack.push(element);
            } else { // 优先级小于栈顶运算符,则弹出
                tmp = stack.pop();
                suffix.append(tmp).append(" ").append(element).append(" ");
            } 
        }
        
        // 把栈中剩余运算符都弹出
        while (stack.size() > 0) {
            suffix.append(stack.pop()).append(" ");
        }
        
        return suffix.toString();
    }
    
    /**
     * 根据后缀表达式算出结果
     * eg:中缀表达式8+(9-1)*8+7/2
     * 后缀表达式8 9 1 - 8 * + 7 2 / +,元素之间之间用空格分隔。
     * 从左到右遍历后缀表达式
     * 遇到数字就进栈
     * 遇到符号,就将栈顶的两个数字出栈运算,运算结果进栈,直到获得最终结果。
     * -----------目前只支持整数,由于整数相除会出现浮点数,这里用String作为返回值--------
     * @param expression 后缀表达式
     * @return 结果
     */
    private static String suffixToValue(String expression) {
        // 已经用空格分隔,直接分割
        String[] suffix = expression.split(" ");
        stack.clear();        
        double num1 = 0, num2 = 0; // 注意次序,num2在栈顶,整数运算结果也可能是double
        String tmp = "";
        
        for (int i = 0; i < suffix.length; i++) {
            if (isNum(suffix[i])) { // 数字
                stack.push(suffix[i]);
            } else{ // 是操作符
                num2 = Double.parseDouble(stack.pop());
                num1 = Double.parseDouble(stack.pop());
                tmp = calculate(num1, num2, suffix[i]);
                if (ERROR_ZERO.equals(tmp)) {
                    throw new ArithmeticException("被除数不能为0");
                } else {
                    stack.push(tmp);
                }
            }
        }
        
        // 最终结果也压在栈中,取出即可
        return stack.pop();
    }
    
    /**
     * 目前只支持整数,但是整数运算结果也可能是double,故这里传入的参数用double。
     * @param num1 第一个数,在前
     * @param num2 第二个数,在后
     * @param operator 运算符
     * @return 运算结果,除数为0则返回Error
     */
    private static String calculate(double num1, double num2, String operator) {
        double result = 0;
        switch (operator) {
        case "+":
            result = num1 + num2;
            break;
        case "-":
            result = num1 - num2;
            break;
        case "*":
            result = num1 * num2;
            break;
        case "/":
            if (num2 == 0){ // 除数为0
                return ERROR_ZERO;
            }
            result = 1.0 * num1 / num2;
            break;
        default:
            break;
        }
        return String.valueOf(result);
    }
    
    /**
     * 检测字符是不是数字
     * 浮点数其实可以写零宽断言,但是
     * @param c 待检测字符
     * @return 检测结果
     */
    private static boolean isNum(char c) {
        if ( (c >= '0' && c <= '9') || c == '.' ) {
            return true;
        }
        return false;
    }
    
    /**
     * 检测字符串是不是数字
     * @param str 待检测字符串
     * @return 检测结果
     */
    private static boolean isNum(String str) {
        Matcher matcher = pattern.matcher(str);
        if (matcher.matches()) {
            return true;
        }
        return false;
    }
    
    /**
     * 获取栈顶运算符
     * @return 如果栈中无运算符,则返回空字符串
     */
    private static String getTopOperator() {
        String tmp = "";
        for (int i = stack.size() - 1; i >= 0; i--) {
            tmp = stack.get(i);
            if ("(".equals(tmp)) {
                break;
            } else if (isOperator(tmp)) {
                return tmp;
            }
        }
        return "";
    }
    
    /**
     * 检测一个字符是不是运算符
     * 栈中不是运算符就是括号
     * @param str 待检测字符,由于可能有多位数字字符串,这里用的是String
     * @return 检测结果
     */
    private static boolean isOperator(String str) {
        for (int i = 0; i < operator.length; i++) {
            if (operator[i].equals(str)) {
                return true;
            }
        }
        return false;
//        // 也可以如下:
//        return Arrays.asList(operator).contains(str);
    }
    
    /**
     * 获取表达式的下一个运算符或数字,同时从表达式中删除该元素
     * @param expression 表达式
     * @return
     */
    private static String popNextElement(StringBuilder expression) {
        StringBuilder result = new StringBuilder();
        char c = expression.charAt(0);
        expression.deleteCharAt(0);
        result.append(c);
        
        if (isNum(c)) {
            // 如果第一次取到的是数字,则继续检查
            while ( expression.length() >0 && isNum(c = expression.charAt(0))) {
                expression.deleteCharAt(0);
                result.append(c);
            }
        }

        return result.toString();
    }
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

posted @ 2015-09-28 17:08  包清骏  阅读(1290)  评论(0编辑  收藏  举报