【Java數據結構與算法】 前綴中綴後綴表達式及轉換

前綴表達式(波蘭表達式)

前綴表達式分析與介紹

  1. 前綴表達式又稱爲波蘭式,前綴表達式的運算符位於操作數之前
  2. 舉例說明:(3+4)*5-6對應的前綴表達式就是 - * + 3 4 5 6

前綴表達式的計算機求值
從右至左掃描表達式,遇到數字時,將數字壓入堆棧,遇到運算符時,彈出棧頂的兩個數,用運算符對他們做相應的計算(棧頂元素和次頂元素),並將結果入棧;重複上述過程直達表達式最左端,最後運算的得出的值即爲表達式的結果

思路分析

例如(3+4)*5-6對應的前綴表達式就是 - * + 3 4 5 6,針對前綴表達hi求值步驟如下:

  1. 右至左掃描,將6,5,4,3壓入堆棧;
  2. 遇到+運算符。因此彈出3和4(3爲棧頂元素,4爲次頂元素),計算出3+4的值,得7,再將7入棧;
  3. 接下來是*運算符,因此彈出7和5,計算7*5=35,將35入棧;
  4. 最後是-運算符,計算出35-6的值,即29,由此得出最終結果;

中綴表達式

中綴表達式分析與介紹

  1. 中綴表達式就是最常見的運算表達式如:(3+4)*5-6
  2. 中綴表達式的求值是我們人最熟悉的,但是對計算機來說卻不好操作(參考:【Java數據結構與算法】棧及經典應用),因此,在計算結果時,往往將中綴表達式轉成其它的表達式來操作(一般轉成後綴表達式

後綴表達式(逆波蘭表達式)

後綴表達式分析與介紹

  1. 後綴表達式又稱爲逆波蘭表達式,與前綴表達式相似,只是運算符位於操作數之後
  2. 舉例說明:(3+4)*5-6對應的後綴表達式就是3 4 + 5 * 6 -
  3. 再比如:
正常的表達式 逆波蘭表達式
a + b a b +
a + (b - c) a b c - +
a + (b - c) * d a b c - d * +
a + d * (b - c) a d b c - * +
a = 1 + 3 a 1 3 + =

後綴表達式的計算機求值
從左到右掃描表達式,遇到數字時,將數字壓入堆棧,遇到運算符時,彈出棧頂的兩個數,用運算符對它們做相應的計算(次頂元素和棧頂元素),並將結果入棧;覆上述過程直達表達式最左端,最後運算的得出的值即爲表達式的結果

思路分析逆波蘭計算器

例如(3+4)*5-6對應的前綴表達式就是3 4 + 5 * 6 -,針對後綴表達式求值步驟如下:

  1. 從左到右掃描,將3和4壓入堆棧;
  2. 遇到+運算符,因此彈出4和3(4爲棧頂元素,3爲次頂元素),計算出3+4的值,得7,再將7入棧;
  3. 將5入棧;
  4. 接下來是運算符,因此彈出5和7,計算出75=35,將35入棧;
  5. 最後是-運算符,計算出35-6的值,即29,由此得出最終結果;

代碼實現逆波蘭計算器

package DataType;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class PolandNotation {
    public static void main(String[] args){
        //先定義一個逆波蘭表達式
        //(3+4)*5-6 =>3 4 + 5 * 6 -
        //說明爲了方便,逆波蘭表達式的數字和符號使用空格隔開
        String suffixExpression = "3 4 + 5 * 6 -";
        //思路
        //1.先將"3 4 + 5 * 6 -"放到Array List種
        //2.將Array list傳遞給一個方法,遍歷Array list配合棧完成計算
        List<String> rpnList = getListString(suffixExpression);
        System.out.println("rpnList=" + rpnList);
        int res = calculate(rpnList);
        System.out.println("計算的結果是=" + res);
    }
    //將一個逆波蘭表達式,依次將數據和運算符放入Array list中
    public static List<String> getListString(String suffixExpression){
        //將suffixExpression分割
        String[] split = suffixExpression.split(" ");
        List<String> list = new ArrayList<String>();
        for (String ele : split){
            list.add(ele);
        }
        return list;
    }
    //完成對逆波蘭表達式的運算
    /*
    1.從左到右掃描,將3和4壓入堆棧
    2.遇到+運算符,因此彈出4和3(4爲棧頂元素,3爲次頂元素),計算出3+4的值,得7,再將7入棧
    3.將5入棧
    4.接下來是*運算符,因此彈出5和7,計算出7*5=35,將35入棧
    5.最後是-運算符,計算出35-6的值,即29,由此得出最終結果
     */
    public static int calculate(List<String> ls){
        //創建棧,只需要一個棧
        Stack<String> stack = new Stack<String>();
        //遍歷ls
        for (String item : ls){
            //這裏使用正則表達式來取出數
            if (item.matches("\\d+")){//匹配的是多位數
                //入棧
                stack.push(item);
            }else {
                //pop出兩個數,並運算,再入棧
                int num2 = Integer.parseInt(stack.pop());
                int num1 = Integer.parseInt(stack.pop());
                int res = 0;
                if (item.equals("+")){
                    res = num1 + num2;
                }else if (item.equals("-")){
                    res = num1 - num2;
                }else if (item.equals("*")){
                    res = num1 * num2;
                }else if (item.equals("-")){
                    res = num1 / num2;
                }else {
                    throw  new RuntimeException("運算符有誤~~");
                }
                //把res入棧
                stack.push(res + "");
            }
        }
        //最後留在stack中的結果就是答案
        return Integer.parseInt(stack.pop());
    }
}

這就是後綴表達式的運算過程,但如果我們只知道中綴表達式不想進行人工轉化應該怎麼計算呢?

中綴表達式轉換爲後綴表達式

可以看到,後綴表達式適合計算式進行運算,但是人卻不太容易寫出,尤其是表達式很長的情況,因此在開發中,我們需要將中綴表達式轉化成後綴表達式

思路分析

中綴表達式轉後綴表達式的思路步驟分析:

  1. 初始化兩個棧:運算符棧s1和儲存中間結果的棧s2;
  2. 從左至右掃描中綴表達式;
  3. 遇到操作數時,將其壓入s2;
  4. 遇到運算符時,比較其與s1棧頂運算符的優先級:
    1. 如果s1爲空,或棧頂運算符爲左括號"(",則直接將此壓入運算符入棧;
    2. 否則,若優先級比棧頂運算符高,也將運算符壓入s1;
    3. 否則,將s1棧頂的運算符彈出並壓入到s2中,再次轉到(4-1)與s1中新的棧頂運算符相比較。
  5. 遇到括號時:
    1. 如果是左括號,則直接壓入s1;
    2. 如果是右括號,則依次彈出s1棧頂的運算符,並壓入s2,直到遇到左括號爲止,此時將這一對括號丟棄;
  6. 重複步驟2-5,直到表達式的最右邊
  7. 將s1中剩餘的運算符依次彈出並且壓入s2
  8. 依次彈出s2中的元素並且輸出,結果的逆序即爲中綴表達式對應的後綴表達式。

舉例:中綴表達式1+((2+3)*4)-5 =>後綴表達式

掃描到的元素 s2(棧頂->棧底) s1(棧底->棧頂) 說明
1 1 數字,直接入棧
+ 1 + s1爲空,運算符直接入棧
( 1 + ( 左括號,直接入棧
( 1 + ( ( 同上
2 1 2 + ( ( 數字
+ 1 2 + ( ( + s1棧頂爲左括號。運算符直接入棧
3 1 2 3 + ( ( + 數字
) 1 2 3 + + ( 右括號,彈出運算符直至遇到左括號
* 1 2 3 + + ( * s1棧頂爲左括號。運算符直接入棧
4 1 2 3 + 4 + ( * 數字
) 1 2 3 + 4 * + 右括號,彈出運算符直至遇到左括號
- 1 2 3 + 4 * + - -與+優先級相同,因此彈出+,再壓入-
5 1 2 3 + 4 * + 5 - 數字
到達最右端 1 2 3 + 4 * + 5 - s1中剩餘的運算符

代碼實現

package DataType;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class PostfixExpression
{
    public static void main(String[] args) {
        //完成將一箇中綴表達式轉成後綴表達式
        //1.因爲直接對字符串進行操作不方便,因此先將中綴表達式字符串轉成中綴表達式對應的list
        //2.將得到的中綴表達式對應的list轉成後綴表達式對應的list
        //即Array list [1, +, (, (, 2, +, 3, ), *, 4, ), -, 5] =>Array list後綴表達式
        String expression = "1+((2+3)*4)-5";
        List<String> infixExpressionList = toInfixExpressionList(expression);
        System.out.println("中綴表達式對應的list=" + infixExpressionList);
        List<String> suffixExpreesionList = parseSuffixExpreesionList(infixExpressionList);
        System.out.println("後綴表達式對應的list=" + suffixExpreesionList);
    }
    //即Array list [1, +, (, (, 2, +, 3, ), *, 4, ), -, 5] =>Array list後綴表達式
    //方法:將得到的中綴表達式對應的list轉成後綴表達式對應的list
    public static List<String> parseSuffixExpreesionList(List<String> ls){
        //定義兩個棧
        Stack<String> s1 = new Stack<String>();//符號棧
        //因爲s2這個棧,在整個過程中,沒有pop操作,而且還得逆序輸出,很麻煩,索引就不用棧了,直接使用list
        List<String> s2 = new ArrayList<String>();//存儲中間結果的lists2
        //遍歷ls
        for (String item : ls){
            //如果是一個數就加入到s2
            if (item.matches("\\d+")){
                s2.add(item);
            }else if (item.equals("(")){
                s1.push(item);
            }else if(item.equals(")")){
                //如果是右括號,則依次彈出s1棧頂的運算符,並壓入s2,直到遇到左括號爲止,此時將這一對括號丟棄
                while (!s1.peek().equals("(")){
                    s2.add((s1.pop()));
                }
                s1.pop();//!!!!!!!!將小括號刪除
            }else {
                //當item的優先級小於等於s1棧頂運算符的優先級,將s1棧頂的運算符彈出並加入到s2中,再次轉到(4-1)與s1中新的棧頂運算符相比較。
                //問題:缺少比較優先級高低的方法
                while (s1.size()!=0 && Operation.getValue(s1.peek()) >= Operation.getValue(item)){
                    s2.add(s1.pop());
                }
                //還需要將item壓入棧
                s1.push(item);
            }
        }
        //將s1中剩餘的運算符依次彈出並且壓入s2
        while (s1.size() != 0){
            s2.add(s1.pop());
        }
        return s2;//因爲存放的是list,所以正常輸出就行了
    }
    //方法:因爲直接對字符串進行操作不方便,因此先將中綴表達式字符串轉成中綴表達式對應的list
    public static List<String> toInfixExpressionList(String s){
        //定義一個list存放中綴表達式對應的內容
        List<String> ls = new ArrayList<String>();
        int i = 0;//這是個指針,用於遍歷中綴表達式字符串
        String str;//對多位數的拼接
        char c;//每遍歷到一個字符,就放入到c
        do {
            //如果c是一個非數字,需要加入到ls中
            if ((c=s.charAt(i)) < 48 || (c=s.charAt(i)) >57){
                ls.add("" + c);
                i ++;//i需要後移
            }else {//如果是一個數,需要考慮多位數的問題
                str = "";//先將str 置成""
                while (i < s.length() && (c=s.charAt(i)) >= 48 && (c=s.charAt(i)) <= 57){
                    str += c;//拼接
                    i ++;
                }
                ls.add(str);
            }
        }while (i < s.length());
        return ls;//返回
    }
}
//編寫一個類Operation,可以返回一個運算符對應的優先級
class Operation{
    private static int ADD = 1;
    private static int SUB = 1;
    private static int MUL = 1;
    private static int DIV = 1;
    //寫一個方法返回對應的優先級
    public static int getValue(String operation){
        int result = 0;
        switch (operation){
            case "+":
                result = ADD;
                break;
            case "-":
                result = SUB;
                break;
            case "*":
                result = MUL;
                break;
            case "/":
                result = DIV;
                break;
            default:
                System.out.println("不存在該運算符~");
                break;
        }
        return result;
    }
}

畢竟編程我是初學者,難免有理解錯誤的地方,希望大家看完之後,發現錯誤可以評論出來,謝謝大家

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