【算法】用栈实现逆波兰计算器

逆波兰计算器就是使用逆波兰表达式实现的计算器

1.什么是逆波兰表达式

  • 逆波兰表达式又叫后缀表达式
  • 那什么又叫后缀表达式?
  • 举例说明:(3+4)×5-6 就是中缀表达式,其对应的后缀表达式就是3 4 + 5 × 6 -,再例如:
    【算法】用栈实现逆波兰计算器_第1张图片
  • 有中缀有后缀就有前缀,(3+4)×5-6的前缀表达式就是: - × + 3 4 5 6,针对此前缀表达式的求值步骤如下:
    【算法】用栈实现逆波兰计算器_第2张图片
  • 简单来说,中缀表达式就是我们常见的计算表达式,对我们人类很友好,但对计算机不友好,相反,后缀表达式则就对计算机很友好,所以我们在实现逆波兰计算器的时候,第一步就是把中缀表达式转为后缀表达式,然后再做运算操作。
  • 具体逆波兰表达式的运算规则拿(3+4)×5-6 对应的后缀表达式:3 4 + 5 × 6 -举例如下:
    【算法】用栈实现逆波兰计算器_第3张图片
  • 至于更详细的说明请自行百度。

2.用栈实现逆波兰计算器

package com.data.algorithm.reversePolishNotation;


import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Stack;
import java.util.regex.Pattern;

/**
 * 用栈实现逆波兰计算器
 *  主要步骤:
 *   一,先把中缀表达式转为后缀表达式
 *      1.1 初始化两个栈,运算符栈s1,存储中间结果栈s2
 *      1.2 从左至右扫描中缀表达式
 *      1.3 遇到操作数时把其压入s2
 *      1.4 遇到运算符时,用其与s1栈顶运算符比较优先级
 *           1.4.1 如果s1为空,或栈顶运算符为左括号“(”,则直接将此运算符入s1.
 *           1.4.2 如果s1不为空,栈顶运算符也不为左括号“(”,则与s1栈顶运算符比较优先级,若比s1栈顶优先级高,也将其压入s1.
 *           1.4.3 再否则,如果不比s1栈顶优先级高,就把s1栈顶运算符弹出并压入s2,然后再重复1.4与s1新栈顶比较
 *      1.5 遇到括号时:
 *           1.5.1 如果是“(”,则直接压入s1
 *           1.5.2 如果是“)”,则依次弹出s1栈顶运算符压入s2,直到遇到“("为止,此时将这对括号丢弃
 *      1.6 重复步骤1.2--1.5直至到表达式最右边
 *      1.7 最后将s1剩余运算符依次弹出压入s2
 *      1.8 依次弹出s2中元素,其逆序就是此中缀表达式对应的后缀表达式
 *   二,按照逆波兰表单式的运算规则计算结果,以(3*4)*5-6为例,其后缀表达式为:3 4 + 5 * 6 -,计算步骤如下:
 *      2.1 从左至右扫描,将3和4压入栈s3
 *      2.2 遇到“+”,弹出3和4,计算3+4得7,将7再入栈s3
 *      2.3 继续扫描,将5入栈
 *      2.4 遇到“*”,弹出5和7,计算7*5,得35,将35入栈
 *      2.5 继续扫描,将6入栈
 *      2.6 最后是“-",计算35-6,得29,这就是最终结果
 * @author wangjie
 * @version V1.0
 * @date 2020/3/1
 */
public class ReversePolishCalculator {


    /**
     * 判断是否是数字
     */
    private static final Pattern isNumber = Pattern.compile("^[-\\+]?[.\\d]*$");

    /**
     *
     * @param str
     * @return
     */
    public static Double calculator(String str){

        Double doub = 0.0D;
        try {
            //1,校验参数
            check(str);
            //2,转为逆波兰表达式
            List<String> list = toReversePolish(str);
            //3,计算结果
            doub = doCalc(list);
        }catch(Exception e){
            e.printStackTrace();
        }
        return doub;
    }

    /**
     * 计算结果
     * @param list
     * @return
     */
    private static Double doCalc(List<String> list) {
        Double d = 0.0D;

        if (list.size() == 1) {
            //如果只剩一个数字
            System.out.println(list);
            d = Double.valueOf((String)list.get(0));
            return d;
        } else {
            ArrayList<String> list1 = new ArrayList();

            for(int i = 0; i < list.size(); ++i) {
                list1.add((String)list.get(i));
                if (isSymbol((String)list.get(i))) {
                    Double d1 = doTheMath((String)list.get(i - 2), (String)list.get(i - 1), (String)list.get(i));
                    list1.remove(i);
                    list1.remove(i - 1);
                    list1.set(i - 2, "" + d1);
                    list1.addAll(list.subList(i + 1, list.size()));
                    break;
                }
            }

            doCalc(list1);
            return d;
        }
    }

    /**
     * 根据字符集判断运算符,进而计算
     * @param s1
     * @param s2
     * @param symbol
     * @return
     */
    private static Double doTheMath(String s1, String s2, String symbol) {
        Double result;
        switch(symbol.hashCode()) {
            case 42:
                if (symbol.equals("*")) {
                    result = Double.valueOf(s1) * Double.valueOf(s2);
                    return result;
                }
                break;
            case 43:
                if (symbol.equals("+")) {
                    result = Double.valueOf(s1) + Double.valueOf(s2);
                    return result;
                }
                break;
            case 45:
                if (symbol.equals("-")) {
                    result = Double.valueOf(s1) - Double.valueOf(s2);
                    return result;
                }
                break;
            case 47:
                if (symbol.equals("/")) {
                    result = Double.valueOf(s1) / Double.valueOf(s2);
                    return result;
                }
        }

        result = null;
        return result;
    }
    /**
     * 参数校验
     * @param str
     */
    private static void check(String str){

        if(null != str && !"".equals(str.trim())){
            new RuntimeException("data is empty");
        }
        if(isNotNumber(String.valueOf(str.charAt(0)))){
            throw new RuntimeException("data illeagle,start not with a number");
        }
    }

    /**
     * 判断字符串是否是数字
     * @param str
     * @return
     */
    private static boolean isNotNumber(String str) {
        return !isNumber.matcher(str).matches();
    }

    /**
     * 中缀表达式转为后缀表达式
     * @param str
     * @return
     */
    private static List<String> toReversePolish(String str){

        List<String> data = Collections.synchronizedList(new ArrayList());
        Stack<String> stack = new Stack();
        str = replaceAllBlank(str);
        int start = 0;
        //遍历中缀表达式字符串
        for(int i = 0; i < str.length(); ++i) {
            String each;
            //如果不是运算符
            if (!isSymbol(String.valueOf(str.charAt(i)))) {
                //如果是最后一个字符或下个字符是运算符
                if (i == str.length() - 1 || isSymbol(String.valueOf(str.charAt(i + 1)))) {
                    each = start == 0 ? str.substring(start, i + 1) : str.substring(start + 1, i + 1);
                    if (isNotNumber(each)) {
                        throw new RuntimeException("data not match number");
                    }

                    data.add(each);
                }
            } else {
                each = String.valueOf(str.charAt(i));
                //如果是运算符“+-*/”或“(”
                if (!stack.isEmpty() && !"(".equals(each) && (calcLevel(each) <= calcLevel((String)stack.peek()) || calcLevel(each) >= 2147483647)) {
                    if (!stack.isEmpty() && calcLevel(each) <= calcLevel((String)stack.peek())) {
                        while(!stack.isEmpty() && calcLevel(each) <= calcLevel((String)stack.peek()) && calcLevel((String)stack.peek()) != 2147483647) {
                            data.add((String)stack.pop());
                        }

                        stack.push(each);
                    } else if (")".equals(each)) {
                        while(!stack.isEmpty() && 2147483647 >= calcLevel((String)stack.peek())) {
                            if (2147483647 == calcLevel((String)stack.peek())) {
                                stack.pop();
                                break;
                            }

                            data.add((String)stack.pop());
                        }
                    }
                } else {
                    stack.push(each);
                }

                start = i;
            }
        }

        Collections.reverse(stack);
        data.addAll(new ArrayList(stack));
        System.out.println(data);
        return data;

    }

    /**
     * 判断运算符优先级
     * @param s
     * @return
     */
    private static int calcLevel(String s) {
        if (!"+".equals(s) && !"-".equals(s)) {
            return !"*".equals(s) && !"/".equals(s) ? 2147483647 : 2;
        } else {
            return 1;
        }
    }

    /**
     * 判断是否运算符
     * @param s
     * @return
     */
    private static boolean isSymbol(String s) {
        return s.matches("\\+|-|\\*|/|\\(|\\)");
    }

    /**
     * 去除所有空格
     * @param s
     * @return
     */
    private static String replaceAllBlank(String s) {
        return s.replaceAll("\\s+", "");
    }

    public static void main(String[] args) {
        //定义一个中缀表达式字符串
        String str = "22.9 + (2+9.8)*4/5 +8-9";
        calculator(str);
    }


}

你可能感兴趣的:(java,算法,数据结构)