后綴表達式適合計算機式的計算,因此在開發中,我們需要將中綴表達式轉為后綴表達式。
三種表達式
這里再次區分一下前綴表達式、中綴表達式、后綴表達式(以(3+4)*5-6為例)
中綴表達式就是我們正常遇見的(3+4)*5-6這樣的式子
前綴表達式又稱為波蘭式,其運算符是在數之前
中綴表達式轉前綴表達式思路:從右至左掃描表達式,遇到數字時,將數字壓入堆棧,遇到運算符時,彈出棧頂的兩個數,用運算符對它們做相應的計算(棧頂元素 top 次頂元素),並將結果入棧;重復上述過程直到表達式最左端,最后運算得出的值即為表達式的結果
例如:- * + 3 4 5 6
- 從右至左掃描,將6、5、4、3壓入堆棧
- 遇到+運算符,因此彈出3和4(3為棧頂元素,4為次頂元素,注意與后綴表達式做比較),計算出3+4的值,得7,再將7入棧
- 接下來是×運算符,因此彈出7和5,計算出7×5=35,將35入棧
- 最后是-運算符,計算出35-6的值,即29,由此得出最終結果
中綴表達式轉后綴表達式具體步驟
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"轉換為后綴表達式為:"1 2 3 + 4 * + 5 -"
過程如下:
舉例代碼
package com.atxihua; import java.util.ArrayList; import java.util.List; import java.util.Stack; public class PolandNotation1 { public static void main(String[] args) { //完成將一個中綴表達式轉成后綴表達式的功能 //1+((2+3)*4)-5轉成1 2 3 + 4 * + 5 - //因為直接對str進行操作,不方便,因此,先將1+((2+3)*4)-5存入對應的list //即1+((2+3)*4)-5=》ArrayList[1,+,(,(,2,+,3,),*,4),-,5] //將得到的中綴表達式對應的List=》后綴表達式對應的List //即ArrayList[1,+,(,(,2,+,3,),*,4),-,5]=>ArrayList[1,2,3,+,4,*,+,5,-] String expression="1+((2+3)*4)-5"; List<String> infixExpreesionList=toInfixExpreesionList(expression); System.out.println("中綴表達式="+infixExpreesionList);//ArrayList[1,+,(,(,2,+,3,),*,4),-,5] List<String> suffixExpreesionList=parseSuffixExpreesionList(infixExpreesionList); System.out.println("后綴表達式="+suffixExpreesionList);//ArrayList[1,2,3,+,4,*,+,5,-] System.out.println("結果為:"+calculate(suffixExpreesionList)); } //將中綴表達式轉成對應的list private static List<String> toInfixExpreesionList(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++; }else {//如果是一個數,需要考慮多位數 str="";//先將str設置為0[48]-9[57] 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; } //將得到的中綴表達式對應的List=>后綴表達式 private static List<String> parseSuffixExpreesionList(List<String> ls) { //定義兩個棧 Stack<String> s1=new Stack<String>();//符號棧 //說明:s2這個棧,在整個轉換過程中,沒有pop操作,而且后面需要逆序輸出,就不使用棧,直接使用list存儲 //Stack<String> s2=new Stack<String>();//數棧 List<String> s2=new ArrayList<String>();//存儲中間結果的list //遍歷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();//將 ( 彈出s1棧,消除小括號 }else { //當item的優先級小於等於s1棧頂運算符,將s1棧頂運算符彈出並加入到s2,直到遇到左括號為止,此時將這一對括號丟棄 //我們缺少一個比較優先級高低的方法 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;// } private 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("-")){ //注意num2比num1先出棧,所以是num1-num2 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()); } } //編寫一個類Operation可以返回一個運算符,對應的優先級 class Operation{ private static int Add=1; private static int SUB=1; private static int MUL=2; private static int DIV=2; //寫一個方法,返回對應的優先級數字 public static int getValue(String operation){ int result=0; if(operation.equals("+")){ result=Add; }else if(operation.equals("-")){ result=SUB; }else if(operation.equals("*")){ result=MUL; }else if(operation.equals("/")){ result=DIV; }else { System.out.println("不存在該運算符"); } return result; } }
運行結果
因為我們在寫代碼時,遇到左括號時,壓入棧中,在遇到右括號時在處理,因此,計算機將左括號當作運算符處理,在處理邏輯中只對加減乘除進行了處理,相信這點很容易理解,就不再處理了。