Java--栈实现计算器

1. 简介

通过栈实现的一个简易的计算器,利用了逆波兰表达式。

支持的功能:

  1. 支持 + - * / ()
  2. 多位数,支持浮点数、超大数
  3. 兼容处理,过滤任何空白字符,包括空格、制表符、换页符

2. 计算原理

1. 逆波兰表达式计算过程

以(3+4)*5-6为例 后缀表达式:3 4 + 5 * 6 -

  1. 从左至右扫描,将3和4压入堆栈;
  2. 遇到+运算符,因此弹出4和3(4为栈顶元素,3为次顶元素),计算出3+4的值,得7,再将7入栈;
  3. 将5入栈;
  4. 接下来是×运算符,因此弹出5和7,计算出7×5=35,将35入栈;
  5. 将6入栈;
  6. 最后是-运算符,计算出35-6的值,即29,由此得出最终结果

2. 中缀表达式转后缀表达式(波兰表达式转逆波兰)

1. 实现思路

  1. 初始化两个栈:运算符栈operaStack和储存中问结果的rsStack;
  2. 从左至右扫捕中缀表达式:
  3. 遇到操作数时,将其压rsStack
  4. 遇到运算符时,比较其与operaStack栈顶运算符的优先级:
    1. 如果operaStack为空,或栈顶运算符为左括号"("则直接将此运算符入栈operaStack
    2. 否则,若优先级比栈顶运算符的高,也将运算符压入operaStack
    3. 否则;将operaStack栈顶的运算符弹出并压入到s2中,再次转到4)2步, 和operaStack中新的栈顶运算符相比较:
  5. 遇到括号时:
    1. 如果是左括号“(”,则直接压入operaStack
    2. 如果是右括号“)”, 则依次弹出operaStack栈顶的运算符,并压入s2,直到遇到左括号为 止,此时将这一对括号丢弃
  6. 重复步骤2至5, 直到表达式的最右边
  7. 将operaStack中剩余的运算符依次弹出并压入s2
  8. 依次弹出s2中的元索并输出,结果的逆序即为中缀表达式对应的后级表达式

3. 代码实现

/**
     * 将普通的中缀表达式转为后缀表达式,并封装List集合
     * @param oldExpression
     * @return
     */
    public static List<String> transform(String oldExpression){
        Stack<String> rsStack = new Stack<>();    //记录中间结果
        Stack<String> operaStack = new Stack<>(); //记录运算符
        String temp = "";
        String[] expressionArray = oldExpression.split("");

        for (String item : expressionArray) {
            // 是数字
            if (item.matches("\\d|\\.")){
                temp += item; //扫描多位数
                if (item.equals(expressionArray[expressionArray.length-1])){
                    rsStack.push(temp);
                    temp = "";   // 新修改内容
                }
            }else { // 不是数字,是操作符
                // 如果temp不为空,证明多位数扫描完毕
                if (isNotNull(temp)){
                    rsStack.push(temp);
                    temp = "";
                }

                if (isOpe(item)){
                    if (operaStack.isEmpty() || "(".equals(operaStack.peek())){
                        operaStack.push(item);
                    }else {
                        while (priority(item) <= priority(operaStack.peek())){
                            rsStack.push(operaStack.pop());
                            if (operaStack.isEmpty()){
                                break;
                            }
                        }
                        operaStack.push(item);
                    }
                }else if ("(".equals(item)){
                    operaStack.push(item);
                }else if (")".equals(item)){
                    while (!"(".equals(operaStack.peek())){
                        rsStack.push(operaStack.pop());
                    }
                    operaStack.pop();
                }
                //@TODO 抛出异常,表达式存在非法字符
            }
        }
        while (!operaStack.isEmpty()){
            rsStack.push(operaStack.pop());
        }

        List<String> list = new ArrayList<>();
        for (String s : rsStack) {
            list.add(s);
        }

        return list;
    }

3. 计算器源码

1. 主类源码

import java.math.BigDecimal;
import java.util.List;
import java.util.Stack;

/**
 * 功能:
 *      1. 支持 + - * / ()
 *      2. 多位数,支持浮点数、超大数
 *      3. 兼容处理,过滤任何空白字符,包括空格、制表符、换页符
 * @author 叶子
 * @Description 计算器
 * @DevelopmentTools IntelliJ IDEA
 * @Data 2020/12/8 星期二 16:38
 */
public class Calculator {

    public static void main(String[] args) {
        String expression = "(3+4)*5-6.1";

        System.out.println(calculator(expression));
    }

    /**
     * 根据逆波兰表达式进行计算
     * @param expression
     * @return
     */
    public static String calculator(String expression){
        List<String> expressionList = CalculatorUtil.transform(expression);
        Stack<String> stack = new Stack<>();

        for (String item : expressionList) {

            if (item.matches("^-?[1-9]\\d*$") || item.matches("^-?([1-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*|0?\\.0+|0)$")){
                stack.push(item);
            }else {
                BigDecimal num2 = new BigDecimal(stack.pop());
                BigDecimal num1 = new BigDecimal(stack.pop());
                String temp = CalculatorUtil.compute(num1,num2,item).toString();

                stack.push(temp);
            }
        }

        return stack.pop();
    }
}

2. 封装的工具类源码

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

/**
 * @author 叶子
 * @Description 计算器工具类
 * @DevelopmentTools IntelliJ IDEA
 * @Data 2020/12/8 星期二 16:38
 */
public class CalculatorUtil {
    /**
     * 将普通的中缀表达式转为后缀表达式
     * @param oldExpression
     * @return
     */
    public static List<String> transform(String oldExpression){
        oldExpression = oldExpression.replaceAll("\\s+","");
        Stack<String> rsStack = new Stack<>();    //记录中间结果
        Stack<String> operaStack = new Stack<>(); //记录运算符
        String temp = "";
        String[] expressionArray = oldExpression.split("");

        for (String item : expressionArray) {
            // 是数字
            if (item.matches("\\d|\\.")){
                temp += item; //扫描多位数
                if (item.equals(expressionArray[expressionArray.length-1])){
                    rsStack.push(temp);
                }
            }else { // 不是数字,是操作符
                // 如果temp不为空,证明多位数扫描完毕
                if (isNotNull(temp)){
                    rsStack.push(temp);
                    temp = "";
                }

                if (isOpe(item)){
                    if (operaStack.isEmpty() || "(".equals(operaStack.peek())){
                        operaStack.push(item);
                    }else {
                        while (priority(item) <= priority(operaStack.peek())){
                            rsStack.push(operaStack.pop());
                            if (operaStack.isEmpty()){
                                break;
                            }
                        }
                        operaStack.push(item);
                    }
                }else if ("(".equals(item)){
                    operaStack.push(item);
                }else if (")".equals(item)){
                    while (!"(".equals(operaStack.peek())){
                        rsStack.push(operaStack.pop());
                    }
                    operaStack.pop();
                }else {
                    throw new RuntimeException("非法字符!");
                }
            }
        }
        while (!operaStack.isEmpty()){
            rsStack.push(operaStack.pop());
        }

        List<String> list = new ArrayList<>();
        for (String s : rsStack) {
            list.add(s);
        }

        return list;
    }

    /**
     * 判断是否为合法运算符
     * @param operation
     * @return
     */
    public static boolean isOpe(String operation){
        return "+".equals(operation) || "-".equals(operation) || "*".equals(operation) || "/".equals(operation);
    }

    /**
     * 计算运算符优先级
     * 返回值越大,优先级越高
     * 返回值为-1,说明是不是合法运算符
     * @param ope
     * @return
     */
    public static int priority(String ope){
        if ("+".equals(ope) || "-".equals(ope)){
            return 0;
        }else if ("*".equals(ope) || "/".equals(ope)){
            return 1;
        }else {
            return -1;
        }
    }

    /**
     * 判断字符串是否为空
     * @param target
     * @return
     */
    public static boolean isNotNull(String target){
        return (target!=null) && (!"".equals(target));
    }

    /**
     * 根据传递的数和运算符完成运算
     * @param num1
     * @param num2
     * @param opera
     * @return
     */
    public static BigDecimal compute(BigDecimal num1, BigDecimal num2, String opera){
        BigDecimal res = new BigDecimal("0");

        switch (opera){
            case "+":
                res = num1.add(num2);
                break;
            case "-":
                res = num1.subtract(num2);
                break;
            case "*":
                res = num1.multiply(num2);
                break;
            case "/":
                res = num1.divide(num2);
                break;
            default:
                break;
        }

        return res;
    }
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值