1. 程式人生 > >java棧應用之表示式求值

java棧應用之表示式求值

原始碼的github地址,可以下載到本地執行

package stack.demo;



import java.io.IOException;
import java.util.Scanner;
import java.util.Stack;

/**
 * 表示式求值 算符優先法
 * 3*(5-2)#  #在這裡表示結尾
 *
 * 思路:
 * 使用兩個棧,分別是運算元棧 儲存數字 和操作符棧 儲存運算子
 * 讀入表示式時
 * 如果是運算元 則入運算元棧
 * 如果是運算子 則入操作符棧
 * 當運算子入棧時,和操作符棧棧頂元素比較優先順序
 * 如果優先順序比棧頂元素高,則入棧,並接收下一個字元
 * 如果和棧頂元素相同,則脫括號 並接收下一個字元 ( 因為相同只有( )括號)
 * 如果小於棧頂元素優先順序,則操作數出棧,操作符出棧 並計算運算結果再入棧
 *
 * 關鍵點:迴圈的退出條件 直到運算全部結束,即當前棧頂元素和讀入的操作符均為#
 *
 * 例子:
 * 3*(5-2)#
 *
 * 算符優先順序:
 * + -按順序先後,先來優先順序大於後來的,即從左到右依次計算
 * * / 優先順序大於+ - , 與* /比較則 先來的優先順序大於後來的
 * + - * / 優先順序均大於(  小於)
 */
public class EvaluateExpression {

    private static Stack<Character> stackOPR = new Stack<>(); //操作符棧
    private static Stack<Integer> stackOPN = new Stack<>();//運算元棧

    public static void main(String[] args) throws IOException {

        char c = (char) System.in.read();
        stackOPR.push('#');
        while (c != '#' ||  stackOPR.peek() != '#') {
            if (isOPN(c)) { //判斷是否是運算元
                //是 則入運算元棧 ,讀入下一個
                stackOPN.push(c-48); //asc碼 '0'轉為int是48  A是65 a是97
                c = (char) System.in.read();
            } else {
                switch (isPrior(c)) {
                    case '>':  //優先順序比棧頂元素大 ,則入棧,並接收下一個字元
                        stackOPR.push(c);
                        c = (char) System.in.read();
                        break;
                    case '='://優先順序和棧頂元素相同
                        stackOPR.pop(); //脫去括號
                        c = (char) System.in.read();
                        break;
                    case '<': //優先順序比棧頂元素小,則操作數出棧,操作符出棧,運算之後入棧。即意思是優先順序高的先運算
                        int b = stackOPN.pop();//順序靠後的運算元
                        int a = stackOPN.pop();//順序靠前的運算元
                        stackOPN.push(Option(a, b, stackOPR.pop()));
                        break;
                }
            }
        }
        System.out.println("計算結果為:"+stackOPN.pop());
    }

    //返回和棧頂元素優先順序的比較結果,> 表示優先順序大於棧頂元素 <  表示優先順序小於棧頂元素 =表示優先順序相等
    static char isPrior(Character c) {
        char c2 = stackOPR.peek();
        if (c == '+' || c == '-') { //c為後進入的操作符,c2為之前進入的操作符
            //如果同為+ - 則c2的優先順序大於c  同理 * /就更不用說了
            if (c2 == '+' || c2 == '-' || c2 == '*' || c2 == '/') {  //所以和棧頂元素相比 優先順序要小
                return '<';
            }
            if (c2 == '(') {
                return '>';
            }
            if (c2 == ')') {
                return '<';
            }
            if (c2 == '#') {
                return '>'; //#是定義為預算結束的標誌符 比所有的操作符優先順序都小
            }
        }
        if (c == '*' || c == '/') {
            if (c2 == '+' || c2 == '-') {
                return '>';
            }

            if (c2 == '*' || c2 == '/') {
                return '<';
            }
            if (c2 == '(') {
                return '>';
            }
            if (c2 == ')') {
                return '<';
            }
            if (c2 == '#') {
                return '>';
            }
        }


        if (c == '(') {
            return '>';
        }
        if (c == ')') {
            if (c2 != '(') {
                return '<';
            } else {
                return '=';
            }
        }

        if (c == '#') {
            if (c2 != '#') {
                return '<';
            } else {
                return '=';
            }
        }
        return 0;
    }


    static int Option(int a, int b, Character c) {
        switch (c) {
            case '+':
                return a + b;
            case '-':
                return a - b;
            case '*':
                return a * b;
            case '/':
                return a / b;
        }
        return 0;
    }

    static boolean isOPN(char c) {
        if (c == '+' || c == '-' || c == '*' || c == '/'||c=='('||c==')'||c=='#') {
            return false;
        } else {
            return true;
        }
    }
}

輸出結果:
3*(7-2)#
計算結果為:15

難點在於
1.表示式的優先順序區分,要點為更高優先順序的要先計算,比如說3x(1+2),這裡(的優先順序就要比x高,因為要先算括號裡面的,而後面的+的優先順序又要比(高,但是比)小。
2.迴圈的退出條件 直到運算全部結束,即當前棧頂元素和讀入的操作符均為#

原始碼的github地址,可以下載到本地執行