上一篇也提到,棧其實是一種很重要的數據結構,下面簡單講解下棧是如何實現四則運算的。
在此之前,需要說明的是,很多編程語言在進行四則運算的時候,都不是直接運用中綴表達式進行運算的,一般會將中綴表達式轉換為后綴表達式然后利用棧進行具體的運算。因為,計算機無法識別所謂的先乘除后加減的運算順序的,而且,一旦出現括號的表達式,按照中綴表單時處理起來更困難,所以,一般來說,很多編程語言在進行四則運算的時候,都會講中綴轉換為后綴,因為,后綴(前綴也是)表達式有個很好的有點:它沒有括號,結合順序完全是按照符號出現的順序進行的,所以無需考慮過多的其他問題。
一、前綴、中綴以及后綴表達式的概念
1、首先說中綴,它最簡單,就是我們平常寫的算式。1+2*3-(4-2),這個我們平常用的算式書寫方式就是中綴:中,代表符號是位於中間的,它進行和兩邊的數據進行結合並運算得出結果。
2、后綴:符號位於兩個數字之后,例如:1+2的后綴表達式就是1 2+;1+2*3的后綴表達式就是:1(23*)+;如果理解不了,看下面的丑圖理解:
這里要注意的是:符號的結合順序是從最左邊那個符號(這里是*)進行結合的,例如:
123*-3+等價於2*3 - 1 + 3,具體看丑圖;同樣,前綴也遵循類似的原則,只不過它是從右往左結合(即最右邊的符號優先結合)
記住:2*3是顯式加上了括號的,為了方便理解,下面的式子涉及到乘除我也會自動為他們的運算加上括號。
3、前綴:其實理解了后綴,前綴就很好理解了,前綴就是將符號放在數字的前面而已,結合的方式就是符號結合后面兩個數字,例子
2*3 - 1 + 3的前綴表達式為:+-*2 3 1 3(最右邊符號優先結合);
二、中綴和后綴的轉換
例如以下的中綴表達式:((2-(3*5))-(4/2));(注意,算法顯式加上括號)
具體轉化的方法是:遇到數字,直接輸出;遇到左括號,直接忽略;遇到符號,將符號壓入棧,遇到右括號則彈出棧中的符號。
ok,下面的代碼展示了這個過程:
//中綴表單時到后綴表達式的轉換 public String change(String express){ String rtn = ""; char [] expressChar = express.toCharArray(); Stack<Character> stack = new Stack<Character>(); for(int i=0;i<expressChar.length;i++){ if(expressChar[i]<='9' && expressChar[i]>='0'){ //遇到數字,直接輸出 rtn = rtn + expressChar[i]; }else if(expressChar[i]=='*' || expressChar[i]=='-' || expressChar[i]=='+' || expressChar[i]=='/'){ //遇到四則運算的符號,則直接壓入棧 stack.push(expressChar[i]); }else if(expressChar[i]==')'){ //如果是右括號,將棧中的元素pop出來 rtn = rtn + stack.pop(); }else{ //其他元素則忽略(左括號) continue; } } return rtn; }
當然,這種方法有一定的局限性:需要顯式地加上括號才可以轉換成功,所以忽略了一個步驟就是顯式為表達式加上必須的括號。不過這不是我們討論的主題,我們主要是用這個例子說明棧的一個典型作用罷了。
三、利用后綴表達式進行四則運算
在三中,得到了四則運算式子的表達式之后,我們就可以運用棧進行四則運算了,而運算的方法是這樣:在后綴表達式中,我們遇到操作數,則將操作數推進棧中,遇到操作符,則從棧中推出兩個數字進行運算,並同時把運算結果推進棧中即可;循環以上步驟直到所有操作符以及操作數遍歷完畢。
下面簡單貼上該過程的代碼:
//利用后綴表達式進行四則運算 public int operation(String suffix){ char [] suffixChar = suffix.toCharArray(); Stack<Character> stack = new Stack<Character>(); for(int i=0;i<suffixChar.length;i++){ if(suffixChar[i]<'9' && suffixChar[i]>'0'){ stack.push(suffixChar[i]); }else { switch (suffixChar[i]) { case '+': stack.push((char)(stack.pop()-'0' + stack.pop()-'0')); break; case '-': stack.push((char)(stack.pop()-'0' - stack.pop()-'0')); break; case '*': stack.push((char)((stack.pop()-'0') * (stack.pop()-'0'))); break; case '/': stack.push((char)((stack.pop()-'0') / (stack.pop()-'0'))); break; } } } return stack.pop(); }
總而言之,棧是一個非常重要且基礎的數據結構,很多計算機編程語言底層其實就提供了棧的原始實現。例如,我們在嵌套調用函數(例如遞歸)時,就是一個典型的入棧出棧過程:先被調用的函數最后才結束運算;后被調用的函數后面執行,棧充當中間存儲數據和運算狀態的角色,保證運算正常運算下去。