將中綴運算式轉換為逆波蘭運算式並計算結果

看了一下我的播客, 自從加入培訓班後就沒怎麼更新了, 主要還是覺得代碼水準有所提升, 之前寫的一些非常基礎的代碼已經變的沒有太大價值了.

繼續寫這一類代碼, 用處也不大, 以後可能會上一些品質比較高的代碼. 最起碼對我而言還是有一定難度的.

這裡上一段逆波蘭運算式的代碼.

需求是這樣的: 將String math = "12.8 + (2 - 3.55)*4+10/5.0"這個中綴表達式的字符串轉換為逆波蘭表達式(後綴表達式), 並計算出結果.

轉換后的逆波蘭表達式應該是這樣的: 12.8, 2, 3.55, -, 4, *, +, 10, 5.0, /, +

這個代碼涉及到了集合, 和棧的一些基本操作. 這裏就不詳細記錄了. 主要還是記錄一下大體的思路

思路如下:

    1, 定義運算符的優先級, 並將優先級計算方法寫出來.

    2, 掃描中綴表達式,順序進行判斷, 在判斷的過程中用棧存儲計算結果, 用一個集合存儲後綴表達式.

    3, 計算的邏輯是這樣的: 從左往右掃描, 如果第一個拿到的不是括號或者數字, 抛異常, 掃描第一個數字, 并且掃描後面的並判斷,

     如果還是數字, 或者小數點, 拿到后做拼接, 直到掃描到運算符停止, 將拼接的結果入棧; 掃描到第一個符號, 直接入棧,再往下,

     遇到左括號也直接入棧, 繼續掃描, 重複上一個邏輯, 直到遇到右括號, 遇到右括號后, 將前面兩個數彈出, 再從符號棧彈出左括號和一個運算符,

     計算后得到一個數, 再次入棧.(這裏要將左右括號丟掉). 如果上一個邏輯中有多個數和多個運算符, 計算運算符的優先級, 根據優先級做彈棧和計算,

       這樣一直掃描計算, 最終棧裏只剩下一個數字時, 就是計算結果. 縂的來説思路就是判斷是數字還是運算符, 運算符計算優先級,

       計算離優先級高的運算符的進棧數字, 重複這個邏輯到全部掃描計算完.

寫代碼需要注意的是: 思路一定要清晰, 且連貫. 使用循環進行判斷時一定要區分清楚每一個判斷邏輯的進, 出, 終止的條件. 一旦判斷邏輯不正確, 代碼必然出錯.

          切記, 切記

看過的相關視頻資料的大神或許會説我只是搬磚工, 這一點我沒法反駁, 我也不想分辨什麽, 寫這個博客本來的目的也只是做個筆記, 給自己看看, 順帶復習所學

開心就好, o(* ̄▽ ̄*)ブ

package com.stack;

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

public class ReversePolishMultiCalc {
    static final String SYMBOL = "\\+|-|\\*|/|\\(|\\)";
    static final String LEFT = "(";
    static final String RIGHT = ")";
    static final String ADD = "+";
    static final String MINUS = "-";
    static final String TIMES = "*";
    static final String DIVISION = "/";
    static final int LEVEL_01 = 1;
    static final int LEVEL_02 = 2;
    static final int LEVEL_HIGH = Integer.MAX_VALUE;
    
    
    static Stack<String> stack = new Stack<>();
    static List<String> data = Collections.synchronizedList(new ArrayList<String>());
    
    /**
     * 去除所有空白字符
     * @param s
     * @return
     */
    public static String replaceAllBlank(String s) {
        //去空白
        return s.replaceAll("\\s+", "");
    }
    
    /**
     * 判斷是否是數字
     * @param s
     * @return
     */
    public static boolean isNumber(String s) {
        Pattern pattern = Pattern.compile("^[-\\+]?[.\\d]*$");
        return pattern.matcher(s).matches();
    }
    
    /**
     * 判斷運算符
     * @param s
     * @return
     */
    public static boolean isSymbol(String s) {
        return s.matches(SYMBOL);
    }
    
    public static int calLevel(String s) {
        if ("+".equals(s) || "-".equals(s)) {
            return LEVEL_01;
        } else if ("*".equals(s) || "/".equals(s)) {
            return LEVEL_02;
        }
        return LEVEL_HIGH;
    }
    
    public static List<String> doMath (String s) throws Exception {
        if (s == null || "".equals(s.trim())) throw new RuntimeException("表達式不能為空");
        if (!isNumber(s.charAt(0)+"")) throw new RuntimeException("數據類型錯誤");
        
        s = replaceAllBlank(s);
        
        String each;
        int start = 0;
        
        for (int i = 0; i < s.length(); i++) {
            if (isSymbol(s.charAt(i) + "")) {
                each = s.charAt(i) + "";
                //判斷1
                if (stack.isEmpty() || LEFT.equals(each)
                        || ((calLevel(each) > calLevel(stack.peek())) && calLevel(each) < LEVEL_HIGH)) {
                    stack.push(each);
                    //判斷2
                } else if (!stack.isEmpty() && calLevel(each) <= calLevel(stack.peek())) {
                    //棧非空,操作符優先級小於等於棧頂優先級時出棧入集合,直到棧空,或者遇到(,最後操作符入棧
                    while (!stack.isEmpty() && calLevel(each) <= calLevel(stack.peek())) {
                        if (calLevel(stack.peek()) == LEVEL_HIGH) {
                            break;
                        }
                        data.add(stack.pop());
                    }
                    stack.push(each);
                } else if (RIGHT.equals(each)) {
                    //判斷3
                    while (!stack.isEmpty() && LEVEL_HIGH >= calLevel(stack.peek())) {
                        if (LEVEL_HIGH == calLevel(stack.peek())) {
                            stack.pop();
                            break;
                        }
                        data.add(stack.pop());
                    }
                }
                start = i;
            }else if (i == s.length() - 1 || isSymbol(s.charAt(i + 1) + "")) {
                //!!!
                each = start == 0 ? s.substring(start, i + 1) : s.substring(start + 1, i + 1);
                if (isNumber(each)) {
                    data.add(each);
                    continue;
                }
                throw new RuntimeException("表達式類型不匹配數字");
            }
        }
        Collections.reverse(stack);
        data.addAll(new ArrayList<>(stack));
        //測試打印後綴表達式
        System.out.println(data);
        return data;
    }
    //算結果
    public static Double doCalc(List<String> list) {
        Double d = 0d;
        if (list == null || list.isEmpty()) {
            return null;
        }
        if (list.size() == 1) {
            System.out.println(list);
            d = Double.valueOf(list.get(0));
            return d;
        }
        ArrayList<String> list1 = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            list1.add(list.get(i));
            if (isSymbol(list.get(i))) {
                //丟失break會導致運算出錯
                Double d1 = doTheMath(list.get(i - 2), list.get(i - 1), 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;
    }
    
    public static Double doTheMath(String s1, String s2, String symbol) {
//        System.out.println(s1 + "++"+s2 );
        Double result;
        switch (symbol) {
        case ADD:
            result = Double.valueOf(s1) + Double.valueOf(s2);
            break;
        case MINUS:
            result = Double.valueOf(s1) - Double.valueOf(s2);
            break;
        case TIMES:
            result = Double.valueOf(s1) * Double.valueOf(s2);
            break;
        case DIVISION:
            result = Double.valueOf(s1) / Double.valueOf(s2);
            break;
        default : result = null;
        }
        return result;
    }
    
    public static void main(String[] args) {
        String math = "12.8 + (2 - 3.55)*4+10/5.0";
        try {
            doCalc(doMath(math));
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

 

posted @ 2019-09-25 07:18  芻狗工作室  阅读(134)  评论(0编辑  收藏  举报