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

棧的應用--四則運算表示式求值(java語言)

棧的應用–四則運算表示式求值(java語言)

前言

在複習資料結構的過程中,採用單鏈表實現了棧Stack,具體功能有如下幾個功能:判斷其是否為空棧、輸出棧的長度、入棧、出棧並且實現Iterable藉口,可以採用Iterator遍歷棧。在測試了棧之後,覺得應該將棧應用一下,於是在看書大話資料結構中,發現可以將棧應用到四則運算表示式求值中,這樣我就想著去實現一下,想達到的目的是:當輸入一個表示式,例如:9+(3-1)*3+10/2,可以得到其輸出為20.

字尾(逆波蘭)表示法定義

字尾表示法是由波蘭的一位邏輯學家(名字太長,我就不寫了)為了使用計算機解決四則運算問題而提出的,是一種不需要括號的字尾表達法,比如四則運算表示式9+(3-1)×3+10/2,其後綴表示式為931-3×+102/+,叫字尾的原因在於所有的符號都是在要運算數字的後面出現。
單鏈表實現棧的程式碼如下:

package ApplyStack;

import java.util.Iterator;

/**
 * 連結串列表頭操作實現下壓棧
 * 
 * @author 七分帥氣
 * @date 2016.7.20
 * @param <Item>
 */
public class Stack<Item> implements Iterable<Item> {
    private Node first; // 表示連結串列的表頭同時也表示棧頂
    private int N; // 連結串列的長度

    private class Node {
        Item data;
        Node next;

        public
Node(Item data) { this.data = data; } } public boolean isEmpty() { return first == null; } public int size() { return N; } public void push(Item item) { Node oldFirst = first; first = new Node(item); first.next = oldFirst; N++; } public
Item pop() { Item temp = first.data; first = first.next; N--; return temp; } @Override public Iterator<Item> iterator() { return new ListIterator(); } private class ListIterator implements Iterator<Item> { private Node current = first; @Override public boolean hasNext() { return current != null; } @Override public Item next() { Item item = current.data; current = current.next; return item; } @Override public void remove() { } } }

字尾表示式計算結果的規則

規則如下:從左到右遍歷表示式的每個數字和符號,遇到是數字進棧,遇到是符號,就將處於棧頂的兩個數字出棧進行運算,運算結果進棧,一直到最終獲得結果(具體詳見大話資料結構106頁)。
java實現程式碼如下:

class PostToResult{
    private String post;   //中綴表示式轉換得到的字尾表示式
    private Stack<Integer> stack;   //用於得到計算結果的棧

    public PostToResult(String post, Stack<Integer> stack) {
        this.post = post;
        this.stack = stack;
    }

    //由字尾表示式得到四則運算結果的實現過程
    public void operate(){
        String[] strArr = post.split(" ");
        for(int i = 0; i < strArr.length; i++){
            String temp = strArr[i];
            if(isDigital(temp)){
                stack.push(Integer.valueOf(temp));
            }else{
                int result = compute(temp);
                stack.push(result);
            }
        }
    }

    private int compute(String str){
        int re = 0;
        int m = stack.pop();
        int n = stack.pop();
        switch(str){
        case "+" :
            re = n + m;
            break;
        case "-" :
            re = n - m;
            break;
        case "*" :
            re = n * m;
            break;
        case "/" :
            re = n / m;
            break;
        default :
            break;
        }
        return re;
    }

    private boolean isDigital(String str){
        char[] chArr = str.toCharArray();
        int len = chArr.length;
        int count = 0;
        for(int i = 0; i < len; i++){
            if(chArr[i] >= '0' && chArr[i] <= '9')
                count++;
        }
        return count == len;
    }

    public int getResult() {
        return stack.pop();
    }
}

雖然後綴表示式有這樣的優點,可是我們輸入的是中綴表示式,所以需要將中綴表示式轉換為字尾表示式。

中綴表示式轉字尾表示式的規則

規則:從左到右遍歷中綴表示式的每個數字和符號,若是數字就輸出,即成為字尾表示式的一部分;若是符號,則判斷其與棧頂符號的優先順序,是右括號或者優先順序不高於棧頂符號(乘除優先加減)則棧頂元素依次出棧並輸出,並將當前符號進棧,一直到最終輸出字尾表示式為止(具體詳見大話資料結構108頁)。
java實現程式碼如下:

package ApplyStack;
/**
 * 棧的應用:四則運算表示式求值
 * @author 七分帥氣
 * @date 2016.7.20
 */
public class InfixToPost {
    private Stack<String> stack;  //中綴表示式轉換為字尾表示式所需要的棧
    private String infix;    //輸入的中綴表示式
    private String post = "";     //儲存得到的字尾表示式

    //初始化構造器
    public InfixToPost(Stack<String> stack, String infix) {
        this.stack = stack;
        this.infix = infix;
    }

    /**
     * 
     * @param infix 輸入的中綴表示式
     * @return  返回處理過後的中綴表示式,主要是在輸入的中綴表示式加空格
     * 例如:輸入的中綴表示式為:9+(3-1)*3+10/2
     *     輸出的中綴表示式為:9 + ( 3 - 1 ) * 3 + 10 / 2
     */
    private String processInfix(String infix) {
        String result = "";
        for (int i = 0; i < infix.length() - 1; i++) {
            char temp1 = infix.charAt(i);
            char temp2 = infix.charAt(i + 1);
            if (isDigital(temp1) && isDigital(temp2)) {
                result += temp1;
            } else {
                result += temp1 + " ";
            }
        }
        result += infix.charAt(infix.length() - 1); // 將最後一個元素新增進去
        return result;
    }

    private boolean isDigital(char ch) {
        if (ch >= '0' && ch <= '9')
            return true;
        else
            return false;
    }

    //將字首表示式轉換為字尾表示式的處理過程
    public void process() {
        String[] strArr = processInfix(infix).split(" ");
        for (int i = 0; i < strArr.length; i++) {
            String str = strArr[i];
            switch (str) {
            case "+":
            case "-":
                getOperation(str, 1);
                break;
            case "*":
            case "/":
                getOperation(str, 2);
                break;
            case "(":
                stack.push(str);
                break;
            case ")":
                getParent();
                break;
            default:
                post += " " + str;
                break;
            }
        }
        // 數字全部輸出後,需要輸出棧中剩餘的符號
        while (!stack.isEmpty()) {
            post += " " + stack.pop();
        }
    }

    private void getParent() {
        while (!stack.isEmpty()) {
            String top = stack.pop();
            if (top.equals("(")) {
                break;
            } else {
                post += " " + top;
            }
        }
    }

    private void getOperation(String str, int priority) {
        while (!stack.isEmpty()) {
            String top = stack.pop();
            if (top.equals("(")) {
                stack.push(top);
                break;
            } else {
                int priTop = getPriority(top);
                if (priTop < priority) {
                    stack.push(top);
                    break;
                } else {
                    post += " " + top;
                }
            }
        }
        stack.push(str);
    }

    private int getPriority(String str) {
        int pri = 0;
        if (str.equals("+") || str.equals("-")) {
            pri = 1;
        } else {
            pri = 2;
        }
        return pri;
    }

    public String getPost() {
        return post.trim();
    }

    public static void main(String[] args) {

        Stack<String> stack = new Stack<>();
        String input = "9+(3-1)*3+10/2";
        InfixToPost infix = new InfixToPost(stack, input);
        infix.process();

        String post = infix.getPost();
        Stack<Integer> stack_result = new Stack<>();
        PostToResult ptr = new PostToResult(post, stack_result);
        ptr.operate();
        System.out.println(ptr.getResult());
    }
}

以上我按照先把輸入的中綴表示式字串轉換為字尾表示式字串,然後通過後綴表示式字串得到最後的計算結果。整個思路很清晰,但是在實現過程中遇到一個問題就是,當你用字尾表示式去得到結果的過程中,由於數字之間沒有間隔,所以你無法區分數字是一位數還是兩位數,就比如一開始給出的字尾表示式931-3*+102/+,你無法區分是9,3,1還是93,1或者9,13,無法判斷。我的解決方法就是在一開始就對中綴表示式做預處理,將輸入的中綴表示式符號與數字都用空格間隔開,這樣做會使後續得到的字尾表示式能區分這種數字模糊性,於是順理成章地得到最終正確的結果:20.

這是我的理解,如果有問題,希望大家指正。