計算表示式的值c++逆波蘭式實現方法
阿新 • • 發佈:2019-02-02
#include<stack>//棧容器的標頭檔案 #include<iostream> #include<math.h> //數學 標頭檔案 次方函式 using namespace std; int Precedence(char op)//運算子優先順序判斷 { switch(op) { case '+': case '-': return 1; //定義加減運算的優先順序為1 case '*': case '/': return 2; //定義乘除運算的優先順序為2 case '!': case '^': return 3; case '(': case '\0': default: return 0; //定義在棧中的左括號和棧底字元的優先順序為0 } } void stringJudge(char *s)//非法字元判斷 其實 這樣寫 有點蠢 Java已經沒有這樣弄了 { int i=0; while(s[i]!=0) { switch(s[i]) { case'+': case'-': case'*': case'/': case'0': case'1': case'2': case'3': case'4': case'5': case'6': case'7': case'8': case'9': case'.': case'(': case')': case'!': case'^': break; default: throw 3; } i++; } return; } void tonibolan(char *s1,char *s2)//中綴式轉換為逆波蘭式 { stack<char> s;//開一個字元棧 s.push('\0');//棧底字元 int i=0,j=0;//i記錄s1,j記錄s2 char ch=s1[i]; //取首個 if(ch==0)//判斷字串是否是空的 throw 2; if(ch=='.')//判斷首字元是否為小數點 throw 6; if(ch=='-')//負號首位補零 { s2[j++]='0'; //就是在輸出字串中直接加了個0 注意 沒有 讓ch讀下個字元 那個符號還是要處理的 s2[j++]=' '; //空格為了隔開 數與數 } stringJudge(s1); //判斷 非法字元 while(ch!='\0') //棧底字元 over { if(ch==' ') //空格 就 忽略 ch=s1[++i]; //讀下一個 else if(ch=='(') //左括號壓入棧中 { s.push('('); //壓棧 ch=s1[++i]; if(ch=='-')//負號首位補零 { s2[j++]='0'; s2[j++]=' '; } } else if(ch==')') //右括號 把左括號之間的全部出來 依次 寫入 (這個地方改的 優先順序) { while(s.top()!='(') //迴圈至 左括號處 其實 有個bug 如果沒有 左括號的話 自己思考了 { s2[j++]=s.top(); //出棧 s.pop(); //pop()是刪除性出棧 } s.pop(); //最後 刪除 這個 括號 ch=s1[++i]; } else if(ch=='+'||ch=='-'||ch=='*'||ch=='/'|| ch=='!' || ch=='^') //是運算子的話 { char w=s.top(); //出第一個 while(Precedence(w)>=Precedence(ch)) //比較優先順序 { s2[j++]=w; //棧裡的大的話 棧那個寫入 s.pop(); //繼續出棧 這是一個迴圈 w=s.top(); } s.push(ch); //壓入此字元 ch=s1[++i]; if(ch=='+'||ch=='-'||ch=='*'||ch=='/'|| ch=='!' || ch=='^') //這個表示 符號後面又是 符號 丟擲異常 Java中那個 廢棄了這個異常 有另一種方式判斷 throw 4; } else { while((ch>='0' && ch<='9') || ch=='.') //是數字或者小數點 { s2[j++]=ch; //寫入輸出字串(字尾式) ch=s1[++i]; } s2[j++]=' '; //空格 隔開 數 } } ch=s.top(); s.pop(); while(ch!='\0') //將所有的依次取出 寫入 字尾式 '\0'是棧底字元 { if(ch=='(') //說明有多的( 拋異常 throw 1; else { s2[j++]=ch; //寫入 ch=s.top(); s.pop(); } } s2[j++]='\0'; //加上 尾巴 字串尾 } double nibolan(char *s) //這個函式 是 用來 運算 字尾式的 { stack<double> sd; //double棧 int i=0; //迴圈變數 控制位置 double num,num2; //臨時的運算的兩個數 double n1,n2,n3; //同上 while(s[i]!='\0') //迴圈 至尾 { num=0; num2=1; if(s[i]>='0' && s[i]<='9') //如果是數字 整個if是 String 換為 double的作用 C++中自己寫 悲劇啊 { while(s[i]>='0' && s[i]<='9') { // 就是 乘10+新的數字 num*=10; num+=s[i]-'0'; i++; } if(s[i]=='.') //中間的小數點 只能有一個 { i++; while(s[i]>='0' && s[i]<='9') { //就是除10+新的 num2/=10; num+=num2*(s[i]-'0'); i++; } } if(s[i]=='.') //說明小數點多了 拋異常 throw 6; sd.push(num); //將 形成double壓入棧 } else if(s[i]==' ') //忽略 空格 分隔符 i++; else if(s[i]=='+') //以下都是一個意思 { //遇運算子 出棧 一般是兩個數 然後 運算 結果再 壓棧 n1=sd.top(); //出棧 sd.pop(); //刪除 n2=sd.top(); //再出一個 sd.pop(); //再刪除 其實c++的這兩個函式 很麻煩 Java的一個函式 即出也刪 n3=n2+n1; //運算 sd.push(n3); //結果壓棧 i++; //下一個 其實可以再分支的外面寫的 這樣不用 每個都寫 當時沒有注意到 } else if(s[i]=='-') //減法 { n1=sd.top(); sd.pop(); n2=sd.top(); sd.pop(); n3=n2-n1; //注意是哪個減哪個 sd.push(n3); i++; } else if(s[i]=='*') { n1=sd.top(); sd.pop(); n2=sd.top(); sd.pop(); n3=n2*n1; sd.push(n3); i++; } else if(s[i]=='/') { n1=sd.top(); sd.pop(); n2=sd.top(); sd.pop(); if(n1==0) //除數為零 拋異常 throw 5; n3=n2/n1; //也注意是那個 除哪個 sd.push(n3); i++; } else if(s[i]=='!') //階乘 演算法很弱智 { //階乘是隻出一個 n1=sd.top(); sd.pop(); n3=1; if(n1==0) //0!=1 ; else if(n1!=(unsigned int)n1) //double強制轉換無符號int,再與原數比相等 Java都自帶的方法 當年還有自己想 throw 7; //就是 判讀 階乘前的數 是否為 正整數 else { for(int jj=(int)n1;n1>0;n1--) //迴圈 一次減1 就是 階乘的人工 演算法 { n3*=n1; //資料大了 會超 double 很容易的 50!就會了 估計的 } } sd.push(n3); i++; } else if(s[i]=='^') //次方 用的math.h裡面的函式 { n1=sd.top(); sd.pop(); n2=sd.top(); sd.pop(); if(n2<0 && n1!=(int)n1) //這個是次方的數學要求 自己思考了 c++的次方 也要整數 悲劇 throw 8; n3=pow(n2,n1); sd.push(n3); i++; } } if(sd.size()!=1) //如果剩下的元素 大於1個 就說明 數 多了三 於是 拋異常了 throw 4; return sd.top(); //最後的值 為 答案 } int main() { char s1[100]; //最大長度是 固定的 你也可 自己看著辦 char s2[100]; cout<<"支援的符號(半形模式):0123456789 ( ) .(小數點) + - * / !(階乘) ^(次方)"<<endl; //這個裡面你可以溫馨一下 cout<<endl; while(1) { cout<<"請輸入計算式:"; //需要溫馨一下的地方 gets(s1); //字串形式 輸入 try { tonibolan(s1,s2); //轉字尾式 cout<<nibolan(s2)<<endl; //計算 cout<<endl; //打醬油 } catch(int key)//錯誤處理…… { //當年用了 比較蠢的方式 其實可以直接拋字串 不用 在這裡寫了 cout<<endl; if(key==1) cout<<"輸入的計算式括號有誤,請重新輸入"<<endl; else if(key==2) cout<<"輸入的計算式是空的,請重新輸入"<<endl; else if(key==3) cout<<"輸入的式子含有非法字元,請重新輸入"<<endl; else if(key==4) cout<<"輸入的式子格式不正確,請重新輸入"<<endl; else if(key==5) cout<<"0不能作除數,請檢查式子"<<endl; else if(key==6) cout<<"小數點出現錯誤,請檢查式子"<<endl; else if(key==7) cout<<"階乘計算的內容只能為正整數,請檢查式子"<<endl; else if(key==8) cout<<"負數的小數次方無意義,請檢查式子"<<endl; cout<<endl; } } system("pause"); return 0; }