棧的應用——實現簡易計算器(一)


周日晚上心血來潮,想用棧做個簡單的計算器,於是便動手鼓搗了。參照書上的思想,我用了兩個棧來解決這個問題。
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);
}

標題上加了個(一),希望之后會有(二)甚至更多的文章吧,把這個計算器再優化些,功能再豐富些:)

附一張模擬表達式出棧、入棧過程的手稿:
這里寫圖片描述

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM