周日晚上心血來潮,想用棧做個簡單的計算器,於是便動手鼓搗了。參照書上的思想,我用了兩個棧來解決這個問題。
1)棧S1用來存放運算符,棧S2用來存放操作數。由於運算符為char型而操作數為int型,因此,需要使用類模板來聲明兩個不同數據類型的棧。
2)為簡單起見,只能計算0-9之內的數(由於每次只能讀入一個字符入棧,如果是大於等於10的任意數,就要設計另外的算法使“相鄰”的兩個數字組合為一個數)。
3)運算符涉及優先級的問題,為此,需要一個判斷優先級的函數Priority(),該函數返回運算符的優先級。現在的問題是,對於最初輸入以及最后入棧的運算符,它與誰進行比較?為此,先將一符號“#”壓入棧中作為棧底,並且在輸入表達式時以“#”號結尾。一開始我在Priority()函數中令“#”的優先級最低,但之后我發現有更為簡便的方法:在該函數中只要遇到“#”或“(”或“)”,就返回-1,對於“(”還有另一更簡單的處理方式:只要一讀入“(”,就讓其入棧。從代碼中可以知道,這兩種處理方式是不沖突的。
這是本人做的第一個“小程序”,雖然到了這一步仍然只能計算10以內的整數的加減乘除,但是這畢竟是多次調試、改錯並優化后的結果,在這個過程中也得到了很多經驗,對棧的理解也更透徹了。本來想把之前階段性的代碼貼出來,並附上解決方法,但由於沒有保存之前的代碼,那些碰到的問題以及解決方法都無法分享了sigh。
以下為完整代碼:
#include"SeqStack.h" //該頭文件的聲明在另一篇博客中
int Calculate(SeqStack<char>&);
int Priority(char);
int Priority(char op) //判斷輸入的運算符的優先級
{
switch(op){
case '+': case '-':return 1;
case '*': case '/':return 2;
default: return -1;
}
}
int Calculate(SeqStack<char>& S1) //S1為char型的棧,用於存放運算符
{
SeqStack<int>S2; //S2為int型的棧,用於存放操作數
char ch,a,b,c,d,m,n,p,t;
int op1,op2,op3,op4,op5,op6,op7,op8,a1,a2,n1,n2;
S1.push('#'); //先將#存入棧S1中
cout<<"請輸入需要計算的表達式(表達式需以#結尾):\n";
do{
ch = getchar();
switch(ch){
case '0': case '1':
case '2': case '3':
case '4': case '5':
case '6': case '7':
case '8': case '9':
S2.push(ch - '0'); //將char型的數值轉換為int型並壓入棧S2中
break;
case ' ': //若輸入的表達式含有空格,則轉入下一字符
break;
case '+':
if(Priority(S1.get_top()) < Priority('+')) //若當前棧頂運算符優先級低於‘+’,則將當前掃描到的字符‘+’入棧
S1.push('+');
else{
S2.pop(op1);
S2.pop(op2); //將棧S2最外面的兩個數字彈出
if(S1.get_top() == '+') //判斷S1棧頂的運算符,以做出相應的運算
S2.push(op2 + op1);
else if(S1.get_top() == '-')
S2.push(op2 - op1);
else if(S1.get_top() == '*')
S2.push(op2 * op1);
else
S2.push(op2 / op1);
S1.pop(c); //該運算符已做過相應的運算,出棧
S1.push('+'); //將‘+’壓入棧中,作為S1新的棧頂
}
break;
case '-':
if(Priority(S1.get_top()) < Priority('-'))
S1.push('-');
else{
S2.pop(op3);
S2.pop(op4);
if(S1.get_top() == '+')
S2.push(op4 + op3);
else if(S1.get_top() == '-')
S2.push(op4 - op3);
else if(S1.get_top() == '*')
S2.push(op4 * op3);
else
S2.push(op4 / op3);
S1.pop(d);
S1.push('-');
}
break;
case '*':
if(Priority(S1.get_top()) < Priority('*'))
S1.push('*');
else{
S2.pop(op5);
S2.pop(op6);
if(S1.get_top() == '+')
S2.push(op6 + op5);
else if(S1.get_top() == '-')
S2.push(op6 - op5);
else if(S1.get_top() == '*')
S2.push(op6 * op5);
else
S2.push(op6 / op5);
S1.pop(t);
S1.push('*');
}
break;
case '/':
if(Priority(S1.get_top()) < Priority('/'))
S1.push('/');
else{
S2.pop(op7);
S2.pop(op8);
if(S1.get_top() == '+')
S2.push(op8 + op7);
else if(S1.get_top() == '-')
S2.push(op8 - op7);
else if(S1.get_top() == '*')
S2.push(op8 * op7);
else
S2.push(op8 / op7);
S1.pop(m);
S1.push('/');
}
break;
case '(': //只要一遇到左括號,即將其壓入棧中
S1.push('(');
break;
case ')':
do{
S2.pop(n1);
S2.pop(n2);
if(S1.get_top() == '+')
S2.push(n2 + n1);
else if(S1.get_top() == '-')
S2.push(n2 - n1);
else if(S1.get_top() == '*')
S2.push(n2 * n1);
else
S2.push(n2 / n1);
S1.pop(n); //將已參加過運算的運算符出棧
}while(S1.get_top() != '('); //通過該循環保證括號中的所有表達式已計算完畢
S1.pop(p); //更重要的是要將左括號退出 右括號從未進棧
break;
case '#': //輸入的表達式需以#結尾,方便判斷運算符的優先級
S2.pop(a1);
S2.pop(a2);
if(S1.get_top() == '+')
S2.push(a2 + a1);
else if(S1.get_top() == '-')
S2.push(a2 - a1);
else if(S1.get_top() == '*')
S2.push(a2 * a1);
else
S2.push(a2 / a1);
S1.pop(a); //將當前運算符出棧
S1.pop(b); //將棧底的#出棧,至此,棧S1已空。
break;
default:
break;
}
}while(ch != '\n'); //若掃描至換行符,則退出循環,至此,表達式計算完成。
return S2.get_top(); //S2的棧底元素即為最終的結果
}
int main()
{
SeqStack<char>S;
cout<<Calculate(S);
}
標題上加了個(一),希望之后會有(二)甚至更多的文章吧,把這個計算器再優化些,功能再豐富些:)
附一張模擬表達式出棧、入棧過程的手稿:

