1. 程式人生 > >棧的應用-四則運算表示式求值

棧的應用-四則運算表示式求值

Java實現四則運算表示式求值

前言

最近在複習資料結構與演算法,在棧的應用中瞭解到計算機計算四則運算表示式的演算法。

計算機計算四則運算主要分兩步:

  1. 將中綴表示式轉化為字尾表示式;
  2. 將字尾表示式進行運算得出結果。

字尾(逆波蘭)表示式

字尾表示式是一種不包含括號,運算子放在兩個運算物件的後面的表示法,比如四則運算表示式9+(3-1)*3+10/2,其後綴表示式為9 3 1 - 3 * + 10 2 / +。這是計算機採用的形式,計算過程如下:

9 3 1 - 3 * + 10 2 / +

9 2 3 * + 10 2 / +

9 6 + 10 2 / +

15 10 2 / +

15 5 +

20

方法就是:從左到右,將運算子前面的兩個數,分別做為運算數和被運算數,如:3 1 - 就等於(3-1)=2,依次進行計算。

中綴轉字尾

怎樣將中綴表示式轉成字尾表示式?

中綴表示式 “9+(3-1)*3+10/2” 轉化為字尾表示式 “9 3 1 - 3 * + 10 2 / +”

數字輸出,運算子進棧,括號匹配出棧,棧頂優先順序不低出棧

規則:從左到右遍歷中綴表示式的每個數字和符號,若是數字就輸出;是開括號‘(’,直接入棧;若是閉括號‘)’,則依次出棧,匹配前一個開括號;若棧頂運算子的優先順序大於或等於當前運算子,棧頂出棧。

具體過程:

  1. 第一個字元9是數字,輸出“9”,後面是符號“+”,入棧;

  2. 後面是開括號“(”,直接入棧;

  3. 然後是數字3,輸出,表示式變為“9 3”,接著是“-”入棧,然後是數字1,輸出,表示式為“9 3 1”;

  4. 接下來是閉括號“)”,需要去匹配前面的“(”,棧頂依次出棧,並輸出。因此”-“出棧並輸出,表示式為“9 3 1 -”,“(”出棧;

  5. 然後是符號“*”,因為棧頂“+”優先順序低於“*”,所以“*”入棧,後面數字3輸出,表示式變為“9 3 1 - 3”;

  6. 之後是符號“+”,因為棧頂“*”優先順序高於“+”,所以棧中元素出棧(因為沒有比“+”更低的優先順序,所以全部出棧),表示式變為“9 3 1 - 3 * +”,當前符號“+”入棧;

  7. 接著數字10,輸出,表示式變為“9 3 1 - 3 * + 10”;
  8. 然後“/”優先順序高於棧頂“+”,入棧;

  9. 最後是數字2,輸出,表示式為“9 3 1 - 3 * + 10 2”;

  10. 已經到了最後,所以將棧中符號全部出棧並輸出。最終輸出字尾表示式為“9 3 1 - 3 * + 10 2 / +”

計算字尾表示式

中綴轉字尾是將符號進出棧,計算字尾表示式就需要將數字進出棧。

規則就是將數字依次入棧,遇到運算子就出棧兩次,將棧頂元素分別作為被運算數和運算數進行計算,然後將計算結果入棧,直到表示式遍歷完成,運算結束,棧中存在的唯一元素就是表示式的值。

程式碼實現:

import java.util.Scanner;
import java.util.Stack;

public class JavaTest {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        String infix = in.nextLine();
        in.close();
        String postfix = trans2postfix(infix);// 得到字尾表示式
        System.out.println("postfix : " + postfix);
        int count = calculate(postfix);
        System.out.println("count = " + count);
    }

    // 計算字尾表示式
    private static int calculate(String postfix) {
        Stack<Integer> stack = new Stack<>();
        char[] arr = postfix.toCharArray();
        for (int i = 0; i < arr.length; i++) {
            char current = arr[i];// 當前字元
            if (Character.isDigit(current)) {// 如果是數字,入棧
                stack.push(current - 48);
            } else {
                int n1 = stack.pop();
                int n2 = stack.pop();
                int m = 0;
                switch (current) {
                case '+':
                    m = n2 + n1;
                    break;
                case '-':
                    m = n2 - n1;
                    break;
                case '*':
                    m = n2 * n1;
                    break;
                case '/':
                    m = n2 / n1;
                    break;
                default:
                    break;
                }
                stack.push(m);
            }
        }
        return stack.get(0);
    }

    // 中綴轉字尾
    public static String trans2postfix(String infix) {
        StringBuilder sb = new StringBuilder();
        Stack<Character> stack = new Stack<>();
        char[] arr = infix.toCharArray();
        for (int i = 0; i < arr.length; i++) {
            char current = arr[i];// 當前字元
            if (Character.isDigit(current)) {// 如果是數字
                sb.append(current);
            } else {
                if (stack.isEmpty()) {// 如果棧為空,直接入棧
                    stack.push(current);
                } else {
                    if (current == ')') {// 閉括號
                        while (true) {
                            char top = stack.pop();
                            if (top != '(') {
                                sb.append(top);
                            } else {
                                break;
                            }
                        }
                    } else {
                        if (current != '(') {// 開括號,直接入棧
                            while (comparePriority(current, stack.peek()) <= 0) {// 當前優先順序不比棧頂高,出棧
                                char top = stack.pop();
                                sb.append(top);
                                if (stack.isEmpty()) {
                                    break;
                                }
                            }
                        }
                        stack.push(current);
                    }
                }
            }
        }
        while (stack.size() != 0) {
            sb.append(stack.pop());
        }
        return sb.toString();
    }

    // 比較運算子優先順序
    private static int comparePriority(char arg0, char arg1) {
        int pri0 = getPriority(arg0);
        int pri1 = getPriority(arg1);
        if (pri0 < pri1) {
            return 1;
        } else if (pri0 == pri1) {
            return 0;
        } else {
            return -1;
        }
    }

    // 獲取運算子優先順序
    private static int getPriority(char arg0) {
        switch (arg0) {
        case '*':
        case '/':
            return 3;
        case '+':
        case '-':
            return 4;
        default:
            return 0xF;
        }
    }
}

執行結果:

實現過程中遇到一個問題,使用char字元會將整數切成10以內的數字,比如10會變成1和0,而且字尾表示式也沒有做字元間隔,比如931無法區別是9、3、1還是93、1或者931,所以上面的程式碼只能計算10以內的四則運算。優化可以將char改成int,並且輸出要加上間隔。

能力有限,如有不足,請多指教。