C++用字尾表示式(逆波蘭)求四則表示式值,採用STL中的stack
阿新 • • 發佈:2019-01-02
簡介:
20 世紀50 年代, 波蘭邏輯學家JanLukasiewicz ,想到了一種不需要括號的字尾表達法,我們也把它稱為逆波蘭( Reverse Polish Notation, RPN) 表示,對於"如9 + (3 -1 ) X3 +10-/2 " ,如果要用字尾表示法應該是: "9 3 1-3*+10 2 / + " ,這樣的表示式稱為字尾表示式.
中綴表示式轉字尾表示式規則:
從左到右遍歷中綴表示式的每個數字和符號,若是數字就輸出,即成為字尾表示式的一部分; 若是符號,則判斷其與棧頂符號的優先順序,是右括號或優先順序低於棧頂符號(乘除優先加減)則棧頂元素依次出棧並輸出, 並將當前符號進棧,一直到最終輸出字尾表示式為止。
字尾表示式計算求值:
從左到右遍歷表示式的每個數字和符號,遇到是數字就進棧,遇到是符號,就將處於棧頂兩個數字出棧,進行運算,運算結果進錢, 一直到最終獲得結果。
示例程式碼:
<pre name="code" class="cpp">//============================================================================ // Name : SiZeCal.cpp // Author : guo // Version : 0.1 // Copyright : nupt // Description : 四則運算表示式求值,輸入整數的四則運算表示式,計算其值。 // PS:此版本不支援輸入中有空格! //============================================================================ #include <iostream> #include <stack> //use STL #include <stdio.h> #include <string.h> #include <stdlib.h> using namespace std; const int MAXSIZE=256; int InfixToPostfix(char *infix,char *postfix); double Calculate(char *arr); int main() { cout << "四則運算,請輸入運算式:" << endl; // prints 四則運算 char in[MAXSIZE]={0}; char postfix[MAXSIZE]={'\0'}; fgets(in,MAXSIZE,stdin); if(InfixToPostfix(in,postfix)!=1) { cout<<"InfixToPostfix wrong!!!"; return -1; } puts(in);puts(postfix); cout<<Calculate(postfix); return 0; } /* 將中綴表示式轉換為字尾表示式 引數:infix 指向中綴表示式,以回車鍵即\n結尾。 postfix 指向字尾表示式臨時緩衝區,用來存放轉換後的結果。 附轉換規則:從左到右遍歷中綴表示式的每個數字和符號,若是數字則直接儲存在postfix陣列中;若是符號,則判斷其與棧頂符號的優先順序,是右括號或者優先順序不大於棧頂符號,則棧頂元素依次出棧並輸出,直到遇到左括號或者棧空時,才將剛才的那個符號入棧。 */ int InfixToPostfix(char *infix,char *postfix) { stack<char> s; char c,e; int j=0,i=0; c=*(infix+i); //取出中綴表示式中的第一個字元 i++; while('\n'!=c) //遇到換行符,表示轉換結束 { while(c>='0'&&c<='9') //先判斷一下取出的字元是否是數字,如果是數字的話,則直接存入postfix陣列 { postfix[j++]=c; c=*(infix+i); i++; if(c<'0'||c>'9') //如果不是數字,則在後面新增空格,以便區分各個符號 { postfix[j++]=' '; } } if(')'==c) //不是數字,則判斷是否為右括號。[括號的優先順序最高,所以,如果是右括號的話,就得先進行括號裡的各種運算] { e=s.top();s.pop(); while('('!=e) //直到遇到左括號為止 { postfix[j++]=e; postfix[j++]=' '; e=s.top();s.pop(); } } else if('+'==c||'-'==c) //如果是加減號,因為他倆的優先順序最低了,所以此時先將棧裡的所有符號出棧後(除非遇到左括號),再把此符號入棧 { if(!(s.size())) //如果是空棧,則直接將加減號入棧 { s.push(c); } else//如果不是空棧,首先將所有優先順序大於加減的出棧,然後再把加減號入棧 { do{ e=s.top();s.pop(); if('('==e) { s.push(e); } else { postfix[j++]=e; postfix[j++]=' '; } }while(s.size()&&'('!=e); //將棧裡的所有符號出棧(除非遇到左括號) s.push(c); //最後將新來的加減號再入棧 } } else if('*'==c||'/'==c||'('==c) //如果是乘除號或左括號,因為他們的優先順序高,所以直接入棧。 { s.push(c); } else if('\n'==c) //判斷一下,所有符號是否都已轉換完成 { break; } else //能走到這個else的,都是我不認識的符號了 { // printf("\nError:input error,the character %d cann't recognize!\n",c); return -1; } c=*(infix+i); //取出下一個字元進行轉換 i++; } while(s.size()) //轉換完成後,棧裡可能還有沒出棧的運算子號 { e=s.top();s.pop(); postfix[j++]=e; postfix[j++]=' '; } return true; } /* 計算字尾表示式的結果 引數:arr使用空格分隔的字尾表示式字串。例:arr="31 5 + " result 儲存計算完畢後的結果 注:如何利用棧來計算字尾表示式的結果:依次取出字尾表示式中的符號進行比較,如果是數字,則直接入棧;如果是符號,則出棧兩次,彈出兩個要計算的因數,進行計算,之後再將計算結果入棧。知道字尾表示式中所有符號都已比較完畢。 */ double Calculate(char *arr) { // printf("%s\n",arr); double d,e,f; //d,e 存放兩個因數。f存放d,e計算後的結果. stack<double> s; char *op; //存放字尾表示式中的每個因數或運算子 char *buf=arr; //宣告bufhe saveptr兩個變數,是strtok_r函式的需要。 char *saveptr=NULL; while((op=strtok(buf," "))!=NULL) //利用strtok_r函式分隔字串 { buf=NULL; switch(op[0]) { case '+': d=s.top();s.pop(); e=s.top();s.pop(); f=d+e; s.push(f); break; case '-': d=s.top();s.pop(); e=s.top();s.pop(); f=e-d; s.push(f); break; case '*': d=s.top();s.pop(); e=s.top();s.pop(); f=d*e; s.push(f); break; case '/': d=s.top();s.pop(); e=s.top();s.pop(); f=e/d; s.push(f); break; default: d=atof(op); //不是運算子,就肯定是因數了。所以,用atof函式,將字串轉換為double型別 s.push(d); break; } } double result=s.top();s.pop(); return result; }