數組實現棧、中綴計算器、逆波蘭後綴計算器、 中綴轉後綴完整版本!

棧的介紹:

  1. 棧的英文爲(stack)
  2. 棧是一個先入後出(FILO-First In Last Out)的有序列表。
  3. 棧(stack)是限制線性表中元素的插入和刪除只能在線性表的同一端進行的一種特殊線性表。允許插入和刪除的
    一端,爲變化的一端,稱爲棧頂(Top),另一端爲固定的一端,稱爲棧底(Bottom)。
  4. 根據棧的定義可知,最先放入棧中元素在棧底,最後放入的元素在棧頂,而刪除元素剛好相反,最後放入的元
    素最先刪除,最先放入的元素最後刪除
  5. 圖解方式說明出棧(pop)和入棧(push)的概念
    在這裏插入圖片描述

棧的快速入門

  1. 用數組模擬棧的使用,由於棧是一種有序列表,當然可以使用數組的結構來儲存棧的數據內容,
    下面我們就用數組模擬棧的出棧,入棧等操作。
  2. 實現思路分析,並畫出示意圖
    在這裏插入圖片描述

【數據結構與算法】逆波蘭表達式、波蘭表達式

棧的計算器
  • 前綴表達式 (波蘭表達式 從右往左掃描 )
  • 中綴表達式 (正常人計算方式)
  • 後綴表達式 (逆波蘭表達式 從左往右)

1.前綴表達式又稱波蘭式,前綴表達式的運算符位於操作數之前。比如:- × + 3 4 5 6
2.中綴表達式就是常見的運算表達式,如(3+4)×5-6
3.後綴表達式又稱逆波蘭表達式,與前綴表達式相似,只是運算符位於操作數之後,比如:3 4 + 5 × 6

然後我們還需明確一些概念,下面通過我們最熟悉的中綴表達式畫出一棵語法樹來直觀認識一下前後綴表達式的生成。以A+B*(C-D)-E*F爲例:
在這裏插入圖片描述

中綴表達式得名於它是由相應的語法樹的中序遍歷的結果得到的。上面的二叉樹中序遍歷的結果就是A+B*(C-D)-E*F。
前綴表達式是由相應的語法樹的前序遍歷的結果得到的。上圖的前綴表達式爲- + A * B - C D * E F
後綴表達式又叫做逆波蘭式。它是由相應的語法樹的後序遍歷的結果得到的。上圖的後綴表達式爲:A B C D - * + E F * -


下面我們關注幾個點:

  1. 如何用數組實現一個棧
  2. 如果用中綴表達式求出運算結果
  3. 如何根據一個逆波蘭表達式求出運算結果?
  4. 如果將一箇中綴表達式轉換成後綴表達式(逆波蘭表達式)
  
/**
 * 數組實現棧, 中綴計算器,中綴轉後綴, 逆波蘭後綴表達式實現
 */
public class ArrayStackDemo {

    public static void main(String[] args) {
        /** 測試棧*/
        //testStack();

        /** 實現計算器 (中綴表達式)*/
        //calculator();

        /** 逆波蘭表達式實現*/
        //polan();

        /** 中綴表達式轉換後綴表達式*/
        backMiddle();
    }

    /**
     * 中綴轉後綴測試
     */
    private static void backMiddle() {
        /**
         * 1+((2+3)×4)-5 => 轉成  1 2 3 + 4 * + 5 –
         * 思路:
         * 1、把中綴表達式 轉成 轉成字符串存在list裏面
         * 2、1)創建一個棧 存放運算符
         *    2)創建一個棧 存放數據(由於這個棧只是添加不取,並且還要依次打印出來,所以改成list集合)
         *    3)遍歷中綴list
         *       3.1 如果數數直接添加數據list中
         *       3.2 如果運算符棧爲空 或者 棧頂爲 “(”直接入棧
         *       3.3 如果運算符棧頂爲“)”,依次取出放入數據集合中,直到遇到“(”爲止, 並且把“(”取出
         *       3.4 以上3種都不滿足:
         *           如果運算級大於棧頂直接入運算符棧
         *           如果運算級小於等於棧頂,把運算符棧定數據取出來 放入數據集合中,直到遇到大於或者“(”括號不是算符返回,
         *           然後再把這運算符放入運算符棧中
         *  3、把運算符棧中數據依次放入數據集合中。
         *  4、遍歷集合集合出結果
         */
        String expression = "1+((2+3)*4)-5";
        // 把中綴表達式放入list中
        ArrayList<String> list = getChangeList(expression);
        // 轉換
        ArrayList<String> expssion = getExpssion(list);
        System.out.print("中綴:" + expression + "  轉後綴:" + expssion);

    }

    /**
     * 中綴轉後綴過程
     */
    private static ArrayList<String> getExpssion(ArrayList<String> list) {
        // 創建運算符棧
        Stack<String> operStack = new Stack<>();
        ArrayList<String> numList = new ArrayList<>();
        for(int i = 0; i < list.size(); i++) {
            String item = list.get(i);
            if(item.matches("\\d")) {
                // 如果是數直接加入數棧
                numList.add(item);
            }else if(item.equals("(") || operStack.isEmpty()) {
                operStack.push(item);
            }else if(item.equals(")")) {
                // 在符號棧取出符號放入  數棧中 直到遇到(
                while (!operStack.peek().equals("(")) {
                    numList.add(operStack.pop());
                }
                // 取出 (
                operStack.pop();
            }else {
                // 這裏面是運算符
                if(getValue(item) > getValue(operStack.peek())) {
                    // 大於棧頂優先級直接加入
                    operStack.push(item);
                }else {
                    // 取出棧裏的來。放入數棧,再把自己放入數棧
                    while (operStack.size() != 0 && getValue(operStack.peek()) <= getValue(item)){
                        numList.add(operStack.pop());
                    }
                    // 把自己放進去
                    operStack.push(item);
                }
            }
        }

        // 遍歷運算符棧 放入 list中
        for(int i = 0; i < operStack.size(); i++) {
            numList.add(operStack.pop());
        }
        return numList;
    }

    /**
     * 返回運算符等級
     */
    public static int getValue(String oper){
        if("+".equals(oper) || "-".equals(oper)) {
            return 1;
        }else if("*".equals(oper) || "/".equals(oper)) {
            return 2;
        }else {
            return 0;
        }
    }

    /**
     * 後綴轉換list集合
     */
    private static ArrayList<String> getChangeList(String expression) {
        ArrayList<String> list = new ArrayList<>();
        String num = "" ;
        for(int i = 0; i < expression.length(); i++) {
            /**
             * 0 -9  48 49 50 51 52 53 54 55 56 57
             * */
            if(expression.charAt(i) < 48 || expression.charAt(i) > 57 ) {
                // 說明是運算符直接加入姐
                list.add(expression.charAt(i) + "");
            }else {
                // 說明是數字,但是要考慮是多位數情況
                num += expression.charAt(i);
                if(expression.charAt(i) >= 48 && expression.charAt(i) <= 57) {
                    list.add(num);
                    num = "";
                }
            }
        }
        System.out.print(list);
        return list;
    }

    /**
     *  逆波蘭表達式(後綴表達式)
     */
    private static void polan() {
        //String suffixExpression = "(3+4) * 5 - 6 ";
        String suffixExpression = "3 4 + 5 * 6 - ";
        //思路
        //1. 先將 "3 4 + 5 × 6 - " => 放到ArrayList中
        //   案例2:4 * 5 - 8 + 60 + 8 / 2 ==> 逆波蘭: 4 5 * 8 - 60 + 8 2 / +
        //   以案例2看出 掃描到8/2的時候是一個樹,也就是說優先級高的先加進去。
        //   但是計算方法不算。數組直接入棧,遇到運算符取出2個數運算結果,在放入數棧,直到遍歷完成數棧剩下一個。
        //2. 將 ArrayList 傳遞給一個方法,遍歷 ArrayList 配合棧 完成計算

        List<String> list = getListString(suffixExpression);
        System.out.println("rpnList=" + list);
        int res = calculate(list);
        System.out.println("計算的結果是=" + res);
    }

    /**
     * 計算結果返回
     * 1、遍歷list  判斷是數的直接入數棧
     * 2、如果是運算符取出兩個數計算結果,然後在入數棧,直到剩下最後一個結果
     */
    private static int calculate(List<String> list) {
        int result =  0;
        Stack<Integer> stack = new Stack<>();
        for(int i = 0; i < list.size(); i++) {
            String data = list.get(i);
            // 正則表達式, 取出來的數  \d :匹配數字,包括0~9;
            if(data.matches("\\d")) {
                stack.push(Integer.parseInt(data));
            }else {
                int num1 = stack.pop();
                int num2 = stack.pop();
                if(data.equals("+")) {
                    result = num1 + num2;
                }else if(data.equals("-")) {
                    result = num2 - num1;
                }else if(data.equals("*")) {
                    result = num1 * num2;
                }else if(data.equals("/")) {
                    result = num2 / num1;
                }else {
                    System.out.print("符號有問題~~");
                }
                stack.push(result);
            }
        }
        return result;
    }

    /**
     * 截取成list返回
     */
    private static List<String> getListString(String str) {
        ArrayList<String> list = new ArrayList<>();
        String[] strArr = str.split(" ");
        for (String data : strArr){
            list.add(data);
        }
        return list;
    }

    /**
     * 中綴表達式計算過程
     */
    private static void calculator() {
        /**
         * 思路:
         * 1、創建兩個棧,一個爲數棧,一個爲運算符棧
         * 2、獲取的數據判斷是不是運算符,如果不是運算符直接入數棧,
         *    如果是運算符:
         *    1) 優先級小於等於當前運算符號棧,在數棧中取出兩個數,運算符棧中取出一個做運算,把結果在放入數棧中
         *    2) 優先級大於運算符棧頂,直接放入符號棧
         *
         * 3、遍歷取出數據以後,把數棧和運算符棧中數據 取出計算,把結果放入數棧中,直到數棧結構爲1個就是結果。
         *
         *  注意: 如果是多位數,入棧會有問題。判斷條件是下一個爲運算符 才入棧。 期間用變量加一下。
         */
        ArrarStack numStack = new ArrarStack(15);
        ArrarStack operStack = new ArrarStack(15);

        String expression = "4*2-5+4+1";
        // 數棧取的數據2
        int num1 = 0;
        // 數棧取的數據2
        int num2 = 0;
        // 運算符
        int oper = 0;
        // 取出的索引
        int index = 0;
        // 定義變量
        String keepNum = "";
        while (true){
            // 左開右閉,截取字符串,取出第一個字符
            char ch = expression.substring(index, index + 1).charAt(0);
            if(operStack.isOper(ch)) {
                if(operStack.isEmpty()) {
                    // 爲空直接入棧
                    operStack.push(ch);
                }else {
                    // 判斷上一個判斷運算級(注意不是取出)
                    if(operStack.priority(ch) > operStack.priority(operStack.peek())) {
                        // 運算符大於直接如運算符棧
                        operStack.push(ch);
                    }else {
                        // 取出數棧兩個,運算符一個,把計算結果在放入數棧中
                        num1 = numStack.pop();
                        num2 = numStack.pop();
                        oper = operStack.pop();
                        int result = operStack.result(num1, num2, oper);
                        numStack.push(result);
                        // 當前符號符入棧
                        operStack.push(ch);
                    }
                }
            }else {
                // 直接入棧,注意有可能是多位數,這樣加進去就不對了。所以加的時機應該是下一個是運算符在入棧
                keepNum += ch;
                if(index == expression.length() - 1) {
                    // 最後一個直接入數棧
                    numStack.push(Integer.parseInt(keepNum));
                    break;
                }
                char str = expression.substring(index + 1, index + 2).charAt(0);
                if(numStack.isOper(str)) {
                    // 下一個數運算符入棧
                    numStack.push(Integer.parseInt(keepNum));
                    keepNum = "";
                }
            }
                index++;

        }

        //當表達式掃描完畢,就順序的從 數棧和符號棧中pop出相應的數和符號,並運行.
        while (true){
            if(operStack.isEmpty()) {
                break;
            }

            // 取出數棧兩個,運算符一個,把計算結果在放入數棧中
            num1 = numStack.pop();
            num2 = numStack.pop();
            oper = operStack.pop();
            int result = operStack.result(num1, num2, oper);
            numStack.push(result);
        }

        System.out.print("運算結果:" + numStack.pop());


    }

    private static void testStack() {
        ArrarStack stack = new ArrarStack(3);
        Scanner scanner = new Scanner(System.in);
        String key = "";
        boolean loop = true;

        while (loop){
            System.out.println("show: 表示顯示棧");
            System.out.println("exit: 退出程序");
            System.out.println("push: 表示添加數據到棧(入棧)");
            System.out.println("pop: 表示從棧取出數據(出棧)");
            System.out.println("請輸入你的選擇");
            key = scanner.next();

            switch (key) {
                case "s":
                    stack.showStack();
                    break;
                case "push":
                    System.out.println("請輸入一個數");
                    int value = scanner.nextInt();
                    stack.push(value);
                    break;
                case "pop":
                    try {
                        int res = stack.pop();
                        System.out.printf("出棧的數據是 %d\n", res);
                    } catch (Exception e) {
                        // TODO: handle exception
                        System.out.println(e.getMessage());
                    }
                    break;
                case "e":
                    scanner.close();
                    loop = false;
                    break;
                default:
                    break;
            }
        }
    }
}

class ArrarStack{
    private int maxSize;
    private int[] mStack;
    // 棧幀
    private int top = -1;

    public ArrarStack(int maxSize) {
        this.maxSize = maxSize;
        mStack = new int[maxSize];
    }

    /**
     * @param num1  數棧頂1
     * @param num2  數棧頂2
     * @param oper  運算符棧頂1
     * @return
     */
    public int result(int num1, int num2, int oper){
        if(oper == '+') {
            return num1 + num2;
        }else if(oper == '-') {
            return num2 - num1;
        }else if(oper == '*') {
            return num1 * num2;
        }else if(oper == '/') {
            return num2 / num1;
        }else {
            return 0;
        }
    }

    /**
     * 是不是運算符
     */
    public boolean isOper(char ch){
       if(ch == '+') {
           return true;
       }else if(ch == '-') {
           return true;
       }else if(ch == '*') {
           return true;
       }else if(ch == '/') {
           return true;
       }else {
           return false;
       }
    }

    /**
     * 返回優先級
     */
    public int priority(int oper){
        if(oper == '+' || oper == '-') {
            return 1;
        }else if(oper == '*' || oper == '/') {
            return 2;
        }else {
            return -1;
        }
    }

    /**
     * 查詢棧頂數據
     */
    public int peek(){
        return mStack[top];
    }

    /**
     * 入棧
     */
    public void push(int data){
        if(isFull()) {
            System.out.println("棧已滿~~~");
            return;
        }

        top++;
        mStack[top] = data;
    }

    /**
     * 出棧
     */
    public int pop(){
        if(isEmpty()) {
            throw new RuntimeException("棧爲空~~");
        }

        int value = mStack[top];
        top--;
        return value;
    }

    /**
     * 空沒空
     */
    public boolean isEmpty(){
        return top == -1;
    }

    /**
     * 滿沒滿
     */
    public boolean isFull(){
        if(top == maxSize - 1) {
            return true;
        }else {
            return false;
        }
    }

    /**
     * 查詢棧
     */
    public void showStack(){
        if(isEmpty()) {
            throw new RuntimeException("棧爲空~~");
        }

        for(int i = top; i >= 0; i--) {
            System.out.println(mStack[i]);
        }
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章