棧的應(yīng)用之中綴表達(dá)式和后綴表達(dá)式

版權(quán)聲明:本文源自簡(jiǎn)書(shū)tianma,轉(zhuǎn)載請(qǐng)務(wù)必注明出處: http://www.lxweimin.com/p/a0d4764eba18

中綴表達(dá)式: 是一個(gè)通用的算術(shù)或邏輯公式表示方法, 操作符是以中綴形式處于的中間(例:3 + 4),中綴表達(dá)式是人們常用的算術(shù)表示方法,但是不易被計(jì)算機(jī)所解析。
后綴表達(dá)式:是一個(gè)通用的算術(shù)或邏輯公式表示方法, 操作符是后綴形式處于操作數(shù)的后面(例:3 4 +),后綴表達(dá)式雖然不是人們所習(xí)慣的運(yùn)算表示方法,但是易被計(jì)算機(jī)解析。

例如:對(duì)于中綴表達(dá)式 9+(3-1)*2+10/2 , 其后綴表達(dá)式是 9 3 1 - 3 * + 10 2 / + , 那么為了方便計(jì)算機(jī)解析計(jì)算,我們需要將中綴表達(dá)式轉(zhuǎn)換成后綴表達(dá)式,然后再對(duì)后綴表達(dá)式進(jìn)行解析。

1. 中綴表達(dá)式轉(zhuǎn)后綴表達(dá)式:

  1. 當(dāng)讀到一個(gè)操作數(shù)時(shí),立即將它放到輸出中。讀到的是操作符則需要接著判斷是否該入棧。讀到的是左圓括號(hào)則入棧。
  2. 在讀到操作符時(shí),如果棧為空或者棧頂操作符為(,則入棧。如果棧頂操作符不為(,且此操作符優(yōu)先級(jí)小于或等于此時(shí)棧頂操作符,則將棧中元素彈出直至 ①遇到左括號(hào) 或者 ②棧頂元素為更低優(yōu)先級(jí) 或者 ③棧為空為止,并將當(dāng)前操作符入棧;否則當(dāng)前操作符繼續(xù)入棧。操作符中,+-優(yōu)先級(jí)低,*/優(yōu)先級(jí)高。
  3. 如果遇到一個(gè)右括號(hào),那么就將棧中元素彈出并輸出直至遇到左括號(hào)為止。但是這個(gè)左括號(hào)只被彈出,并不輸出。
  4. 如果讀到輸入的末尾,若棧不為空則將棧元素彈出直到該棧變成空棧,并將彈出的符號(hào)寫(xiě)到輸出中。

"9+(3-1)2+10/2" 轉(zhuǎn)換過(guò)程:*

操作過(guò)程 棧中元素 輸出
讀入 9,輸出 9
讀入 +,棧為空,規(guī)則2,入棧 + 9
讀入 ( ,左括號(hào),規(guī)則1,入棧 + ( 9
讀入 3,輸出 + ( 9 3
讀入 -,棧頂為(,規(guī)則2,入棧 + ( - 9 3
讀入 1,輸出 + ( - 9 3 1
讀入 ) ,右括號(hào),規(guī)則3,出棧并輸出 + 9 3 1 -
讀入 *,*優(yōu)先級(jí)高于棧頂+,規(guī)則,2,入棧 + * 9 3 1 -
讀入 3,輸出 + * 9 3 1 - 3
讀入 +,+優(yōu)先級(jí)低于棧頂*,規(guī)則2,棧中元素出棧,當(dāng)前操作符入棧 + 9 3 1 - 3 * +
讀入 10, 輸出 + 9 3 1 - 3 * + 10
讀入 / , /優(yōu)先級(jí)高于+,入棧 + / 9 3 1 - 3 * + 10
讀入 2, 輸出 + / 9 3 1 - 3 * + 10
讀至末尾,規(guī)則4,棧不為空,棧中元素出棧并輸出 9 3 1 - 3 * + 10 / +

2. 后綴表達(dá)式計(jì)算最終結(jié)果:

  1. 從左到右遍歷表達(dá)式的每個(gè)數(shù)字和符號(hào),遇到是數(shù)字則進(jìn)棧,遇到是運(yùn)算符則將棧頂兩個(gè)元素出棧,進(jìn)行運(yùn)算并將運(yùn)算結(jié)果進(jìn)棧;
  2. 遍歷完后綴表達(dá)式,此時(shí)棧中剩余的數(shù)字就是運(yùn)算結(jié)果。

"9 3 1 - 3 * + 10 2 / +" 計(jì)算過(guò)程:

操作過(guò)程 棧中元素
讀入 9,入棧 9
讀入 3,入棧 9 3
讀入 1,入棧 9 3 1
讀入 -,運(yùn)算并將結(jié)果入棧 9 2
讀入 3,入棧 9 2 3
讀入 *,運(yùn)算并將結(jié)果入棧 9 6
讀入 +,運(yùn)算并將結(jié)果入棧 15
讀入 10,入棧 15 10
讀入 2,入棧 15 10 2
讀入 /,運(yùn)算并將結(jié)果入棧 15 5
讀入 +,運(yùn)算并將結(jié)果入棧 20
讀入完畢,棧中元素即為結(jié)果 20

簡(jiǎn)單中綴表達(dá)式計(jì)算的java實(shí)現(xiàn):

public class SimpleCalcutor {

    private class Item {

        private String value;
        private Integer number;

        public Item(String value) {
            this.value = value;
            try {
                number = Integer.parseInt(value);
            } catch (Exception ignore) {
            }
        }

        public boolean isNumber() {
            return number != null;
        }

        public int getNumber() {
            if (isNumber())
                return number;
            throw new NumberFormatException();
        }

        public boolean isAdd() {
            return "+".equals(value);
        }

        public boolean isSub() {
            return "-".equals(value);
        }

        public boolean isMul() {
            return "*".equals(value);
        }

        public boolean isDiv() {
            return "/".equals(value);
        }

        public boolean isLeftBracket() {
            return "(".equals(value);
        }

        public boolean isRightBracket() {
            return ")".equals(value);
        }

        public int getPriority() {
            if (isAdd() || isSub())
                return 0;
            if (isMul() || isDiv())
                return 1;
            throw new RuntimeException("This is not +, -, *, /");
        }

        @Override
        public String toString() {
            return value != null ? value.toString() : null;
        }
    }

    /**
     * 計(jì)算結(jié)果
     * 
     * @param calStr
     * @return
     */
    public int calculate(String calStr) {
        List<Item> infixes = parse(calStr);
        List<Item> postfixes = infix2postfix(infixes);
        return calculateByPostfix(postfixes);
    }

    /**
     * 利用正則表達(dá)式將待計(jì)算的字符串轉(zhuǎn)化為L(zhǎng)ist<Item>形式 ,如 10/2 -> [10, /, 2]
     * 
     * @param calStr
     * @return
     */
    private List<Item> parse(String calStr) {
        Pattern pattern = Pattern.compile("\\D|\\d+");
        Matcher m = pattern.matcher(calStr);
        List<Item> items = new ArrayList<Item>();
        while (m.find()) {
            items.add(new Item(m.group(0)));
        }
        return items;
    }

    /**
     * 中綴表達(dá)式轉(zhuǎn)換為后綴表達(dá)式
     * <p>
     * 1.當(dāng)讀到一個(gè)操作數(shù)時(shí),立即將它放到輸出中。讀到的是操作符則需要接著判斷是否該入棧。讀到的是左圓括號(hào)則入棧。<br>
     * 2.如果遇到一個(gè)右括號(hào),那么就將棧中元素彈出并輸出直至遇到左括號(hào)為止。但是這個(gè)左括號(hào)只被彈出,并不輸出。<br>
     * 3.在讀到操作符時(shí),如果此操作符優(yōu)先級(jí)小于或等于此時(shí)棧頂操作符,則將棧中元素彈出直至(1)遇到左括號(hào)或者(2)棧頂元素為更低優(yōu)先級(jí)或者(3)
     * 棧為空為止。操作符中,'+''-'優(yōu)先級(jí)最低,'('')'優(yōu)先級(jí)最高。 <br>
     * 4.如果讀到輸入的末尾,將棧元素彈出直到該棧變成空棧,將符號(hào)寫(xiě)到輸出中。
     * 
     * @return
     */
    private List<Item> infix2postfix(List<Item> infixes) {
        List<Item> postfixes = new ArrayList<Item>();
        Stack<Item> stack = new Stack<Item>();
        for (Item item : infixes) {
            if (item.isNumber()) {
                postfixes.add(item);
            } else if (item.isRightBracket()) {
                // ) 右括號(hào),將棧中元素彈出直至左括號(hào),且左括號(hào)和右括號(hào)不加入到后綴表達(dá)式中
                while (true) {
                    Item tmp = stack.pop();
                    if (tmp.isLeftBracket())
                        break;
                    postfixes.add(tmp);
                }
            } else if (item.isLeftBracket()) {
                // ( 左括號(hào),將左括號(hào)入棧
                stack.push(item);
            } else {
                // 當(dāng)前操作符為 +, -, *, /,
                if (stack.isEmpty()) {
                    // 操作符棧為空,則將當(dāng)前操作符壓入棧
                    stack.push(item);
                    continue;
                }
                Item top = stack.peek();
                if (top.isLeftBracket()) {
                    // 操作符棧頂為左括號(hào)(,則將當(dāng)前操作符壓入棧
                    stack.push(item);
                    continue;
                }
                if (item.getPriority() <= top.getPriority()) {
                    // 如果此操作符(+,-,*,/)優(yōu)先級(jí)小于或等于此時(shí)棧頂操作符
                    // 則將棧中元素彈出直至(1)遇到左括號(hào)或者(2)棧頂元素為更低優(yōu)先級(jí)或者(3)棧為空為止
                    // 并將彈出的元素加入后綴表達(dá)式中,將當(dāng)前操作符壓入棧中
                    while (true) {
                        Item tmp = stack.peek();
                        if (tmp.isLeftBracket() || tmp.getPriority() < item.getPriority()) {
                            break;
                        }
                        postfixes.add(tmp);
                        stack.pop();
                        if (stack.isEmpty())
                            break;
                    }
                    stack.push(item);
                } else {
                    // 如果當(dāng)前操作符(+,-,*,/)優(yōu)先級(jí)大于此時(shí)棧頂操作符,則將當(dāng)前操作符壓入棧
                    stack.push(item);
                }
            }
        }
        // 如果棧中元素不為空,則將棧中元素全部彈出,加入后綴表達(dá)式中
        while (!stack.isEmpty()) {
            postfixes.add(stack.pop());
        }
        return postfixes;
    }

    /**
     * 通過(guò)后綴表達(dá)式計(jì)算數(shù)值
     * <p>
     * 1. 從左到右遍歷表達(dá)式的每個(gè)數(shù)字和符號(hào),遇到是數(shù)字則進(jìn)棧,遇到是運(yùn)算符則將棧頂兩個(gè)元素出棧,進(jìn)行運(yùn)算并將運(yùn)算結(jié)果進(jìn)棧<br>
     * 2. 遍歷完后綴表達(dá)式,此時(shí)棧中剩余的數(shù)字就是運(yùn)算結(jié)果
     * 
     * @param postfixes
     * @return
     */
    private int calculateByPostfix(List<Item> postfixes) {
        Stack<Integer> stack = new Stack<Integer>();
        for (Item item : postfixes) {
            if (item.isNumber()) {
                stack.push(item.getNumber());
            } else {
                // 運(yùn)算符
                int num1 = stack.pop();
                int num2 = stack.pop();
                int result;
                if (item.isAdd()) {
                    result = num2 + num1;
                } else if (item.isSub()) {
                    result = num2 - num1;
                } else if (item.isMul()) {
                    result = num2 * num1;
                } else if (item.isDiv()) {
                    result = num2 / num1;
                } else {
                    throw new IllegalArgumentException("Operator invalid : " + item.value);
                }
                stack.push(result);
            }
        }
        return stack.pop();
    }

    public static void main(String[] args) {
        SimpleCalcutor calcutor = new SimpleCalcutor();
        String calStr = "9+(3-1)*3+10/2";
        int result = calcutor.calculate(calStr);
        System.out.println(result);
    }

}

源碼github地址:
SimpleCalculator

參考鏈接:
利用棧將中綴表達(dá)式轉(zhuǎn)換成后綴表達(dá)式

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容