java-表达式求值(初级)

2 篇文章 0 订阅

question description 设计一个表达式求值的程序 ,支持+,-,*,/,%,(,)以及=运算符组成的表达式的运算。

假设数字只包含int和float,并且全部的表达式都是正确的。

注意表达式向上cast:

除法 1/3=0,1.0/2=0.5

int+float=float

表达式最后的=可以包含也可以不包含

sample inpput and output
样例1 input: 1+1.0 output:2.0
样例2 input: (55)%11(9+2) output: 33
样例3 input:19.2+8.67/3= output:22.09


import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 

public class Main {
 
    // 表达式字符合法性校验正则模式,静态常量化可以降低每次使用都要编译地消耗
    private static final Pattern EXPRESSION_PATTERN = Pattern.compile("[0-9\\.+-/*()=% ]+");//Pattern.compile("[StringYouNeedToCompare]+")好像是这种用法的格式
 
    // 运算符优先级map
    private static final Map<String, Integer> OPT_PRIORITY_MAP = new HashMap<String, Integer>() {
      // private static final long serialVersionUID = 69684726771458L;//若一个类没有指定serialVersionUID,而且发生了变化,则读取磁盘中的对象时就会报错。
 
        {
            put("(", 0);
            put("+", 2);
            put("-", 2);
            put("*", 3);
            put("/", 3);
            put("%", 3);
            put(")", 7);
            put("=", 20);
        }
    };
 
 
    /**
     ** 输入加减乘除表达式字符串,返回计算结果
     ** @param expression 表达式字符串
     ** @return 返回计算结果
     *      
     */
    public static double executeExpression(String expression) {
        // 非空校验,String.trim()去掉字符串首尾的空格部分
        if (null == expression || "".equals(expression.trim())) {
            throw new IllegalArgumentException("表达式不能为空!");
        }
 
        // 表达式字符合法性校验
        Matcher matcher = EXPRESSION_PATTERN.matcher(expression);
        if (!matcher.matches()) {
            throw new IllegalArgumentException("表达式含有非法字符!");
        }
 
        Stack<String> optStack = new Stack<>(); // 运算符栈
        Stack<BigDecimal> numStack = new Stack<>(); // 数值栈,数值以BigDecimal存储计算,避免精度计算问题
        StringBuilder curNumBuilder = new StringBuilder(16); // 当前正在读取中的数值字符追加器
 
        // 逐个读取字符,并根据运算符判断参与何种计算
        for (int i = 0; i < expression.length(); i++) {
            char c = expression.charAt(i);
            if (c != ' ') { // 空白字符直接丢弃掉
                if ((c >= '0' && c <= '9') || c == '.') {
                    curNumBuilder.append(c); // 持续读取一个数值的各个字符
                } else {
                    if (curNumBuilder.length() > 0) {
                        // 如果追加器有值,说明之前读取的字符是数值,而且此时已经完整读取完一个数值
                    	//将这个数加入数值栈中
                        numStack.push(new BigDecimal(curNumBuilder.toString()));
                        curNumBuilder.delete(0, curNumBuilder.length());
                    }
 
                    String curOpt = String.valueOf(c);//将c转为字符串
                    if (optStack.empty()) {
                        // 运算符栈栈顶为空则直接入栈
                        optStack.push(curOpt);
                    } else {
                        if (curOpt.equals("(")) {
                            // 当前运算符为左括号,直接入运算符栈
                            optStack.push(curOpt);
                        } else if (curOpt.equals(")")) {
                            // 当前运算符为右括号,触发括号内的字表达式进行计算
                            directCalc(optStack, numStack, true);
                        } else if (curOpt.equals("=")) {
                            // 当前运算符为等号,触发整个表达式剩余计算,并返回总的计算结果
                            directCalc(optStack, numStack, false);
                            return numStack.pop().doubleValue();
                        } else {
                            // 当前运算符为加减乘除之一,要与栈顶运算符比较,判断是否要进行一次二元计算
                            compareAndCalc(optStack, numStack, curOpt);
                        }
                    }
                }
            }
        }
 
        // 表达式不是以等号结尾的场景
        if (curNumBuilder.length() > 0) {
            // 如果追加器有值,说明之前读取的字符是数值,而且此时已经完整读取完一个数值
            numStack.push(new BigDecimal(curNumBuilder.toString()));
        }
        directCalc(optStack, numStack, false);
        return numStack.pop().doubleValue();
    }
    
    /**
     * 判断进行运算的数字是否都为整型
     * @param num1
     * @param num2
     * @return 都为整型则返回true,否则返回false
     */
    public static boolean isInt(BigDecimal num1,BigDecimal num2) {
    	boolean isint = false;
    	double n1 = num1.doubleValue();
    	double n2 = num2.doubleValue();
    	if(Math.floor(n1) == n1 &&Math.floor(n2) == n2) {
    		isint = true;
    	}
    	return isint;
    }
 
    /**
     ** 拿当前运算符和栈顶运算符对比,如果栈顶运算符优先级高于或同级于当前运算符,
     ** 则执行一次二元运算(递归比较并计算),否则当前运算符入栈
     ** @param optStack 运算符栈
     ** @param numStack 数值栈
     ** @param curOpt 当前运算符
     *      
     */
    public static void compareAndCalc(Stack<String> optStack, Stack<BigDecimal> numStack,
                                      String curOpt) {
        // 比较当前运算符和栈顶运算符的优先级
        String peekOpt = optStack.peek();
        int priority = getPriority(peekOpt, curOpt);
        if (priority == -1 || priority == 0) {
            // 栈顶运算符优先级大或同级,触发一次二元运算
            String opt = optStack.pop(); // 当前参与计算运算符
            BigDecimal num2 = numStack.pop(); // 当前参与计算数值2
            BigDecimal num1 = numStack.pop(); // 当前参与计算数值1
            BigDecimal bigDecimal = floatingPointCalc(opt, num1, num2);
 
            // 计算结果当做操作数入栈
            numStack.push(bigDecimal);
 
            // 运算完栈顶还有运算符,则还需要再次触发一次比较判断是否需要再次二元计算
            if (optStack.empty()) {
                optStack.push(curOpt);
            } else {
                compareAndCalc(optStack, numStack, curOpt);
            }
        } else {
            // 当前运算符优先级高,则直接入栈
            optStack.push(curOpt);
        }
    }
 
    /**
     ** 遇到右括号和等号执行的连续计算操作(递归计算)
     ** @param optStack 运算符栈
     ** @param numStack 数值栈
     ** @param isBracket true表示为括号类型计算
     *      
     */
    public static void directCalc(Stack<String> optStack, Stack<BigDecimal> numStack,
                                  boolean isBracket) {
        String opt = optStack.pop(); // 当前参与计算运算符
        BigDecimal num2 = numStack.pop(); // 当前参与计算数值2
        BigDecimal num1 = numStack.pop(); // 当前参与计算数值1
        BigDecimal bigDecimal = floatingPointCalc(opt, num1, num2);
 
        // 计算结果当做操作数入栈
        numStack.push(bigDecimal);
 
        if (isBracket) {
            if ("(".equals(optStack.peek())) {
                // 括号类型则遇左括号停止计算,同时将左括号从栈中移除
                optStack.pop();
            } else {
                directCalc(optStack, numStack, isBracket);
            }
        } else {
            if (!optStack.empty()) {
                // 等号类型只要栈中还有运算符就继续计算
                directCalc(optStack, numStack, isBracket);
            }
        }
    }
 
    /**
     ** 不丢失精度的二元运算,支持高精度计算
     ** @param opt
     ** @param bigDecimal1
     ** @param bigDecimal2
     ** @return
     *      
     */
    public static BigDecimal floatingPointCalc(String opt, BigDecimal bigDecimal1,
                                               BigDecimal bigDecimal2) {
        BigDecimal resultBigDecimal = new BigDecimal(0);
        switch (opt) {
            case "+":
                resultBigDecimal = bigDecimal1.add(bigDecimal2);
                break;
            case "-":
                resultBigDecimal = bigDecimal1.subtract(bigDecimal2);
                break;
            case "*":
                resultBigDecimal = bigDecimal1.multiply(bigDecimal2);
                break;
            case "/":
            	
            	if(isInt(bigDecimal1, bigDecimal2)) {
            		int result = bigDecimal1.intValue()/bigDecimal2.intValue(); 
            		resultBigDecimal = BigDecimal.valueOf(result);
            	}
            	else
                resultBigDecimal = bigDecimal1.divide(bigDecimal2, 10, BigDecimal.ROUND_HALF_UP); // 注意此处用法
				/*
				 * 结果不能整除,有余数,这个时候会报java.lang.ArithmeticException:
				 * 
				 * 这边我们要避免这个错误产生,在进行除法运算的时候,针对可能出现的小数产生的计算,必须要多传两个参数
				 * 
				 * divide(BigDecimal,保留小数点后几位小数,舍入模式)
				 */
                break;
            case "%":
            	resultBigDecimal = bigDecimal1.subtract(bigDecimal2.multiply(bigDecimal1.divide(bigDecimal2, 0, BigDecimal.ROUND_DOWN)));
            default:
                break;
        }
        return resultBigDecimal;
    }
 
    /**
     ** priority = 0  表示两个运算符同级别
     ** priority = 1  第二个运算符级别高,负数则相反
     ** @param opt1
     ** @param opt2
     ** @return
     *      
     */
    public static int getPriority(String opt1, String opt2) {
        int priority = OPT_PRIORITY_MAP.get(opt2) - OPT_PRIORITY_MAP.get(opt1);
        return priority;
    }

 
    public static void main(String[] args) {

       
        Scanner sc = new Scanner(System.in);
    	String eps = sc.nextLine();
    	boolean hasdecimal = false;
    	double result = executeExpression(eps);
    	
    	if(Math.floor(result) == result)
    	{
    		System.out.println((int)result);
    	}
    	else {
    		System.out.println(result);
    	}
    		
    	
    }
}

  • 9
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值