中綴表示式轉換為字尾表示式

2021-08-23 12:01:06 字數 4106 閱讀 5098

參考:中綴表示式轉換為字尾表示式

參考:中綴表示式求值問題

一丶中綴表示式求值問題

中綴表示式的求值問題是乙個比較常見的問題之一,我們通常在編寫程式時,直接寫出表示式讓編譯器去處理,很少去關心編譯器是怎麼對表示式進行求值的,今天我們來一起了解一下其中具體的原理和過程。

表示式一般來說有三種:字首表示式、中綴表示式、字尾表示式,其中字尾表示式又叫做逆波蘭表示式。中綴表示式是最符合人們思維方式的一種表示式,顧名思義,就是操作符在運算元的中間。而字首表示式和字尾表示式中操作符分別在運算元的前面和運算元的後面。舉個例子:

3+2這個是最簡單的乙個中綴表示式。而其等同的字首表示式形式為+32,字尾表示式形式為32+。

那麼一些朋友可能會問既然中綴表示式最符合人類的思維習慣,為什麼還需要字首表示式和字尾表示式?先看乙個例子,假如在前面的表示式基礎上加一點東西:

3+2*5

此時的表示式很顯然,如果進行計算,則先計算2*5,最後計算加法。但是如果需要先計算加法運算呢?則必須加上括號,(3+2)*5。

而如果用字尾表示式來表示,則為 32+5*,那麼該表示式的計算順序為3+2 —> (3+2)*5。

區別就在這裡,字尾表示式不需要用括號就能表示出 整個表示式哪部分運算先進行。同理,字首表示式也是如此。這種表示式正好最符合計算機的處理方式,因為字尾表示式和字首表示式求值不需要考慮優先順序的問題,計算機處理起來便簡單很多。

今天我們這裡主要講解中綴表示式和字尾表示式(字首表示式和字尾表示式很類似,就不做過多贅述),

1.中綴表示式直接求值

對於中綴表示式求值來說,一般最常見的直接解決辦法就是利用棧,乙個棧用來儲存運算元,乙個棧用來儲存操作符。

為了簡便起見,暫時表示式中只考慮簡單的+,-,*,/運算,只有圓括號,並且都是整數。

假如有這樣乙個表示式:((3+5*2)+3)/5+6/4*2+3

對於這樣乙個表示式,如果讓你來設計運算元和操作符進棧的出棧的規則,你會怎麼設計?

先不看這麼複雜的表示式,考慮一下簡單點的,還是前面的3+2*5,那麼很顯然先進行乘法運算,後進行加法運算,但是由於操作符在運算元中間,所以當乙個操作符進操作符棧時,該操作符的兩個運算元並沒有都進入到運算元棧中,那麼如何解決呢?只有在後面乙個操作符進操作符棧時,前面的乙個操作符所作用的兩個運算元才會全部進棧。比如3+2*5,棧的變化過程為:

運算元棧:3      運算元棧:3   運算元棧:3 2 

操作符棧:空     操作符棧:+  操作符棧:+    

注意此時遇到操作符「*」,是不是需要彈出運算元棧中的兩個運算元進行運算呢,很顯然不是,因為乘法運演算法比操作符棧的棧頂運算子優先順序高,也就是說當前的操作符在「+」前進行運算,那麼還需要將當前操作符壓棧,則變成:

運算元棧:3 2   運算元棧:3 2 5

操作符棧:+ *  操作符棧:+ *

此時到了表示式的結尾,既然棧頂的操作符的優先順序比棧底的操作符的優先順序高,那麼可以取操作符棧的棧頂操作符和運算元棧的棧頂兩個元素進行計算,則得到2*5=10,(注意從運算元棧先彈出的運算元為右運算元)。此時得到10 ,則應該把10繼續壓到運算元棧中,繼續取操作符棧的棧頂操作符,依次進行下去,則當操作符棧為空時表示計算過程完畢,此時運算元棧中剩下的唯一元素便是整個表示式的值。

再換個例子:2*5+3,這個表示式跟前面表示式的結果雖然相同,但是運算元和操作符入棧和出棧的順序發生了很大變化:

運算元棧:2     運算元棧:2   運算元棧:2 5  

操作符棧:空    操作符棧:*   操作符棧:*     

此時遇到「+」,而操作符棧的棧頂操作符為「*」,棧頂操作符優先順序更高,表示此時可以取操作符棧頂操作符進行運算,那麼棧變成:

運算元棧:10   運算元棧:10 3

操作符棧:空    操作符棧:+

後面的過程跟前面乙個例子類似。

如果複雜一點,比如包含有括號,連續的乘除法這些怎麼處理呢?道理是一樣的,對於左括號直接入棧,碰到右括號,則一直將操作符退棧,直到碰到左括號,則括號中的表示式計算完畢。對於連續的乘除法,跟前面例子中處理過程類似。只需要記住一點:只有當前操作符的優先順序高於操作符棧棧頂的操作符的優先順序,才入棧,否則彈出操作符以及運算元進行計算直至棧頂操作符的優先順序低於當前操作符,然後將當前操作符壓棧。當所有的操作符處理完畢(即操作符棧為空時),運算元棧中剩下的唯一乙個元素便是最終的表示式的值。而操作符的優先順序為:+和-優先順序是一樣的,*和/優先順序是一樣的,+、-的優先順序低於*、/的優先順序。

不過需要注意的是在求值之前需要對表示式進行預處理,去掉空格、識別 負號(區分「-」是作為減號還是負號),提取運算元等。

對於「-」的區分,主要判別方法為:

1)若前乙個字元為『(',則必定為負號;

2)若前乙個字元為')'或者數字,則必定為減號;

3)若前面乙個字元為其他運算子,如*,/,則必定是負號;

3)若前面沒有字元,即該字元為表示式的第乙個字元,則必定是負號。

也就是說只有一種情況下,」-「是作為減號使用的,就是前乙個字元為')'或者數字的時候。

如果判斷出「-」是作為負號使用的,這裡我採用「#」來代替「-」,並將其作為一種運算(優先順序最高)。比如:-3*2

我採取的做法是將"#"入棧,然後當遇到「*」時,由於棧頂操作符為"#",因此取#,然後取運算元棧的棧頂元素(只取乙個)進行運算,然後再把結果壓棧。

/*2014.5.6 測試環境: mingw*/

#include #include #include #include #include #include #include using namespace std;

vectorpreparse(char *str) //對中綴表示式進行預處理,分離出每個token

else

else

break;

}optstack.push(token);

}}

}else if(token=="(")

else if(token==")")

optstack.pop();

}else //如果是運算元,直接入運算元棧

}while(optstack.size()!=0)

return opdstack.top();}

int main(int argc, char *argv)

bintree;

char * preprocess(char *str) //預處理,除去空格,將負號替代為#

else

p->right = createbintree(str,index+1,end);

}return p;}

void preorder(bintree *root)}

void inorder(bintree *root)}

void postorder(bintree *root)}

int main(void)

else

else

break;

}optstack.push(token);

}}

}else if(token=="(")

else if(token==")")

optstack.pop();

}else //如果是運算元,直接入運算元棧

}while(optstack.size()!=0)

return suffix;}

int main(int argc, char *argv)

{ char *str = "((3+5*2)+3)/5+(-6)/4*2+3";

vectorsuffix = tosuffix(str);

int size = suffix.size();

for(int i=0;i測試結果:

最後一種辦法可以很快速地求出中綴表示式對應的字首表示式和字尾表示式,就是添括號去括號法。

比如有表示式: (3+5*2)-2*3

先對每乙個小部分新增括號: ((3+(5*2))-(2*3))

然後將每個操作符放到括號後面:((3(52)*)+(23)*)-

然後去括號:352*+23*-

便得到了字尾表示式,字首表示式類似(只需把操作符放到括號前面即可)。

中綴表示式轉換為字尾表示式

今天我們課前談一談,要說點什麼好呢?最近小甲魚發現,很多魚油在學習資料結構和演算法的時候積極性已經開始有點下降了。甚至很多朋友懷疑資料結構和演算法到底有沒有用?實話說,在大廈的防震設計 消除疾病 防止水源枯竭這些實際問題中,很遺憾,資料結構和演算法幾乎起不到任何直接作用。那為什麼我們要學呢?很簡單,...

中綴表示式轉換為字尾表示式

字尾表示式也叫逆波蘭表示式,其求值過程可以用到棧來輔助儲存。假定待求值的字尾表示式為 6 5 2 3 8 3 則其求值過程如下 1 遍歷表示式,遇到的數字首先放入棧中,此時棧如下所示 2 接著讀到 則彈出3和2,執行3 2,計算結果等於5,並將5壓入到棧中。3 讀到8,將其直接放入棧中。4 讀到 彈...

中綴表示式轉換為字尾表示式

字尾表示式也叫逆波蘭表示式,其求值過程可以用到棧來輔助儲存。假定待求值的字尾表示式為 6 5 2 3 8 3 則其求值過程如下 1 遍歷表示式,遇到的數字首先放入棧中,此時棧如下所示 2 接著讀到 則彈出3和2,執行3 2,計算結果等於5,並將5壓入到棧中。3 讀到8,將其直接放入棧中。4 讀到 彈...