帶你了解Java數(shù)據(jù)結(jié)構(gòu)和算法之前綴,中綴和后綴表達式
1、人如何解析算術(shù)表達式
如何解析算術(shù)表達式?或者換種說法,遇到某個算術(shù)表達式,我們是如何計算的:
①、求值 3+4-5
這個表達式,我們在看到3+4后都不能直接計算3+4的值,知道看到4后面的 - 號,因為減號的優(yōu)先級和前面的加號一樣,所以可以計算3+4的值了,如果4后面是 * 或者 /,那么就要在乘除過后才能做加法操作,比如:
②、求值 3+4*5
這個不能先求3+4的值,因為4后面的*運算級別比前面的+高。通過這兩個表達式的說明,我們可以總結(jié)解析表達式的時候遵循的幾條規(guī)則:
- ①、從左到右讀取算式。
- ②、已經(jīng)讀到了可以計算值的兩個操作數(shù)和一個操作符時,可以計算,并用計算結(jié)果代替那兩個操作數(shù)和一個操作符。
- ③、繼續(xù)這個過程,從左到右,能算就算,直到表達式的結(jié)尾。
2、計算機如何解析算術(shù)表達式
對于前面的表達式 3+4-5,我們?nèi)耸怯兴季S能力的,能根據(jù)操作符的位置,以及操作符的優(yōu)先級別能算出該表達式的結(jié)果。但是計算機怎么算?
計算機必須要向前(從左到右)來讀取操作數(shù)和操作符,等到讀取足夠的信息來執(zhí)行一個運算時,找到兩個操作數(shù)和一個操作符進行運算,有時候如果后面是更高級別的操作符或者括號時,就必須推遲運算,必須要解析到后面級別高的運算,然后回頭來執(zhí)行前面的運算。我們發(fā)現(xiàn)這個過程是極其繁瑣的,而計算機是一個機器,只認(rèn)識高低電平,想要完成一個簡單表達式的計算,我們可能要設(shè)計出很復(fù)雜的邏輯電路來控制計算過程,那更不用說很復(fù)雜的算術(shù)表達式,所以這樣來解析算術(shù)表達式是不合理的,那么我們應(yīng)該采取什么辦法呢?
請大家先看看什么是前綴表達式,中綴表達式,后綴表達式:這三種表達式其實就是算術(shù)表達式的三種寫法,以 3+4-5為例
- ①、前綴表達式:操作符在操作數(shù)的前面,比如 +-543
- ②、中綴表達式:操作符在操作數(shù)的中間,這也是人類最容易識別的算術(shù)表達式 3+4-5
- ③、后綴表達式:操作符在操作數(shù)的后面,比如 34+5-
上面我們講的人是如何解析算術(shù)表達式的,也就是解析中綴表達式,這是人最容易識別的,但是計算機不容易識別,計算機容易識別的是前綴表達式和后綴表達式,將中綴表達式轉(zhuǎn)換為前綴表達式或者后綴表達式之后,計算機能很快計算出表達式的值,那么中綴表達式是如何轉(zhuǎn)換為前綴表達式和后綴表達式,以及計算機是如何解析前綴表達式和后綴表達式來得到結(jié)果的呢?
3、后綴表達式
后綴表達式,指的是不包含括號,運算符放在兩個運算對象的后面,所有的計算按運算符出現(xiàn)的順序,嚴(yán)格從左向右進行(不再考慮運算符的優(yōu)先規(guī)則)。
由于后綴表達式的運算符在兩個操作數(shù)的后面,那么計算機在解析后綴表達式的時候,只需要從左向右掃描,也就是只需要向前掃描,而不用回頭掃描,遇到運算符就將運算符放在前面兩個操作符的中間(這里先不考慮乘方類似的單目運算),一直運算到最右邊的運算符,那么就得出運算結(jié)果了。既然后綴表達式這么好,那么問題來了:
①、如何將中綴表達式轉(zhuǎn)換為后綴表達式?
對于這個問題,轉(zhuǎn)換的規(guī)則如下:
一、先自定義一個棧
package com.ys.poland; public class MyCharStack { private char[] array; private int maxSize; private int top; public MyCharStack(int size){ this.maxSize = size; array = new char[size]; top = -1; } //壓入數(shù)據(jù) public void push(char value){ if(top < maxSize-1){ array[++top] = value; } } //彈出棧頂數(shù)據(jù) public char pop(){ return array[top--]; } //訪問棧頂數(shù)據(jù) public char peek(){ return array[top]; } //查看指定位置的元素 public char peekN(int n){ return array[n]; } //為了便于后面分解展示棧中的內(nèi)容,我們增加了一個遍歷棧的方法(實際上棧只能訪問棧頂元素的) public void displayStack(){ System.out.print("Stack(bottom-->top):"); for(int i = 0 ; i < top+1; i++){ System.out.print(peekN(i)); System.out.print(' '); } System.out.println(""); } //判斷棧是否為空 public boolean isEmpty(){ return (top == -1); } //判斷棧是否滿了 public boolean isFull(){ return (top == maxSize-1); } }
二、前綴表達式轉(zhuǎn)換為后綴表達式
package com.ys.poland; public class InfixToSuffix { private MyCharStack s1;//定義運算符棧 private MyCharStack s2;//定義存儲結(jié)果棧 private String input; //默認(rèn)構(gòu)造方法,參數(shù)為輸入的中綴表達式 public InfixToSuffix(String in){ input = in; s1 = new MyCharStack(input.length()); s2 = new MyCharStack(input.length()); } //中綴表達式轉(zhuǎn)換為后綴表達式,將結(jié)果存儲在棧中返回,逆序顯示即后綴表達式 public MyCharStack doTrans(){ for(int j = 0 ; j < input.length() ; j++){ System.out.print("s1棧元素為:"); s1.displayStack(); System.out.print("s2棧元素為:"); s2.displayStack(); char ch = input.charAt(j); System.out.println("當(dāng)前解析的字符:"+ch); switch (ch) { case '+': case '-': gotOper(ch,1); break; case '*': case '/': gotOper(ch,2); break; case '(': s1.push(ch);//如果當(dāng)前字符是'(',則將其入棧 break; case ')': gotParen(ch); break; default: //1、如果當(dāng)前解析的字符是操作數(shù),則直接壓入s2 //2、 s2.push(ch); break; }//end switch }//end for while(!s1.isEmpty()){ s2.push(s1.pop()); } return s2; } public void gotOper(char opThis,int prec1){ while(!s1.isEmpty()){ char opTop = s1.pop(); if(opTop == '('){//如果棧頂是'(',直接將操作符壓入s1 s1.push(opTop); break; }else{ int prec2; if(opTop == '+' || opTop == '-'){ prec2 = 1; }else{ prec2 = 2; } if(prec2 < prec1){//如果當(dāng)前運算符比s1棧頂運算符優(yōu)先級高,則將運算符壓入s1 s1.push(opTop); break; }else{//如果當(dāng)前運算符與棧頂運算符相同或者小于優(yōu)先級別,那么將S1棧頂?shù)倪\算符彈出并壓入到S2中 //并且要再次再次轉(zhuǎn)到while循環(huán)中與 s1 中新的棧頂運算符相比較; s2.push(opTop); } } }//end while //如果s1為空,則直接將當(dāng)前解析的運算符壓入s1 s1.push(opThis); } //當(dāng)前字符是 ')' 時,如果棧頂是'(',則將這一對括號丟棄,否則依次彈出s1棧頂?shù)淖址?,壓入s2,直到遇到'(' public void gotParen(char ch){ while(!s1.isEmpty()){ char chx = s1.pop(); if(chx == '('){ break; }else{ s2.push(chx); } } } }
三、測試
@Test public void testInfixToSuffix(){ String input; System.out.println("Enter infix:"); Scanner scanner = new Scanner(System.in); input = scanner.nextLine(); InfixToSuffix in = new InfixToSuffix(input); MyCharStack my = in.doTrans(); my.displayStack(); }
四、結(jié)果
五、分析
②、計算機如何實現(xiàn)后綴表達式的運算?
4、前綴表達式
前綴表達式,指的是不包含括號,運算符放在兩個運算對象的前面,嚴(yán)格從右向左進行(不再考慮運算符的優(yōu)先規(guī)則),所有的計算按運算符出現(xiàn)的順序。
注意:后綴表達式是從左向右解析,而前綴表達式是從右向左解析。
①、如何將中綴表達式轉(zhuǎn)換為前綴表達式?
②、計算機如何實現(xiàn)前綴表達式的運算?
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
Java?Hibernate中一對多和多對多關(guān)系的映射方式
Hibernate是一種Java對象關(guān)系映射框架,支持一對多和多對多關(guān)系的映射。一對多關(guān)系可以使用集合屬性和單向/雙向關(guān)聯(lián)來映射,多對多關(guān)系可以使用集合屬性和中間表來映射。在映射過程中,需要注意級聯(lián)操作、延遲加載、中間表的處理等問題2023-04-04SpringBoot配置文件bootstrap和application區(qū)別及說明
這篇文章主要介紹了SpringBoot配置文件bootstrap和application區(qū)別及說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06Java生產(chǎn)1-100的隨機數(shù)簡單實例(分享)
下面小編就為大家?guī)硪黄狫ava生產(chǎn)1-100的隨機數(shù)簡單實例(分享)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-05-05Springboot+Spring Security實現(xiàn)前后端分離登錄認(rèn)證及權(quán)限控制的示例代碼
本文主要介紹了Springboot+Spring Security實現(xiàn)前后端分離登錄認(rèn)證及權(quán)限控制的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-11-11基于maven搭建一個ssm的web項目的詳細(xì)圖文教程
這篇文章主要介紹了基于maven搭建一個ssm的web項目的詳細(xì)教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09JVM內(nèi)存管理之JAVA語言的內(nèi)存管理詳解
下面小編就為大家?guī)硪黄狫VM內(nèi)存管理之JAVA語言的內(nèi)存管理詳解。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08