1. 程式人生 > >【菜鳥er】經典題目_逆波蘭表示法

【菜鳥er】經典題目_逆波蘭表示法

#include <bits/stdc++.h>
using namespace std;
/**寫在前面:
    對於所有容器,屬性判斷一定要放在操作判斷的前面
    也就是說,top出的資料是否等於val這個條件一定要放在能否top後面,否則會異常
*/
stack<string> str;//生成期的臨時符號棧
stack<int> num;//運算期的結果儲存棧
queue<string> rbolan;//生成期的逆波蘭棧
string s;//儲存算式

void str_rbolan();
void rbolan_num();
int fsign(string a);

int main()
{
    str_rbolan();//中綴表示式轉化成逆波蘭表示式
    rbolan_num();//利用逆波蘭表示式計算算式的數值
    int value = num.top();
    cout<<value<<endl;
    return 0;
}
void str_rbolan()
{
    cin>>s;
    for(int i=0;i<s.size();i++){//遍歷算式
        string x;
        x.clear();//清空x
        if(isdigit(s[i])){//如果是數字
            //處理這個數值以字串存入逆波蘭佇列
            while( i<s.size() && isdigit(s[i]) ){
                x = x + s[i];
                ++i;
            }
            i--;
            rbolan.push(x);//數值以字串的形式壓入逆波蘭棧
        }

        else{//不是數字
            x = s[i];//處理x
            if(x == "(")//左括號壓棧
                str.push(x);
            else if(x == ")"){//右括號出棧到左括號
                while( !str.empty() && str.top()!="(" ){
                    rbolan.push( str.top() );
                    str.pop();
                }
                str.pop();//pop出左括號
            }
            //下面處理運算子問題;分為兩種;
            else if( !str.empty() && fsign(x) > fsign(str.top()) )
                str.push(x);
            else{
                while( !str.empty() && fsign(x) <= fsign(str.top() )){
                    rbolan.push(str.top());
                    str.pop();
                }
                str.push(x);
            }
        }
    }
    //把符號棧內的所有字元壓入到逆波蘭棧
    while( !str.empty() ){
        rbolan.push(str.top());
        str.pop();
    }
    return;
}
void rbolan_num()
{
    int x = 0;
    while(!rbolan.empty()){
        x = 0;
        string ss = rbolan.front();//獲取第一個字串進行分析
        if( isdigit(ss[0]) ){//如果是數字,則轉化為十進位制數值
            if(ss.size()>1){
                for(int j=0;j<ss.size();j++)
                    x = x*10 + (int)(ss[j]-'0');
                num.push(x);
            }
            else{
                x = ss[0]-'0';
                num.push(x);
            }
        }
        else{//為運算子
            int b = num.top(); num.pop();
            int a = num.top(); num.pop();

            if(ss == "+")   num.push(a+b);
            else if(ss == "-")   num.push(a-b);
            else if(ss == "*")   num.push(a*b);
            else if(ss == "/")   num.push(a/b);
        }
        rbolan.pop();//向下次操作資料移動
    }
}
int fsign(string a)
{
    if(a=="+") return 1;
    else if(a=="-") return 1;
    else if(a=="*") return 2;
    else if(a=="/") return 2;
}
/**運用逆波蘭表示法求解字串算式表示式的方法:
兩個時期:生成期,運算期
生成期:
    技巧(定義一個臨時符號棧,定義一個逆波蘭棧(為了好用,直接定義成佇列))
    遍歷字串中所有字元
    如果為數字,壓入逆波蘭棧
    如果為左括號,壓入臨時符號棧
    如果為右括號,從臨時符號棧中取出符號壓入逆波蘭棧,直到碰到左括號(左括號直接丟棄)
    如果為運算子
        判斷運算子與臨時符號棧中的運算子優先順序
            如果符號棧內運算子優先順序大 取出到逆波蘭棧,直到符號棧內符號小於待定儲存的運算子
            否則直接壓入符號棧。
    如果有剩餘:依次取出壓入逆波蘭棧,此時臨時符號棧作廢。
運算期:
    補充定義一個結果棧
    依次遍歷逆波蘭棧
    如果取出數字字元:轉化為int壓入結果棧
    如果為運算子:
        從結果棧取出兩個int
        利用運算子運算出結果
        把結果壓入結果棧
*/
/**


1、將一箇中序表示式轉化成為逆波蘭表示式。

       首先維護的是兩個棧,我們這裡暫且稱為S1和S2,S1中的結果最後存的就是逆波蘭表示式,S2中將用於暫時存放運算子並且在最終形成逆波蘭表示式的時候,該棧是會清空的。下面我們看看怎樣具體的形成逆波蘭表示式。

       在此首先定義一下運算子的優先順序關係,從小到達排序,相同優先順序沒有用逗號隔開:(,+-,*\,負號,)。

       從左至右遍歷一個給定的中序表示式,也就是我們常規的數學計算的表示式。

(1)如果遇到的是數字,我們直接加入到棧S1中;

(2)如果遇到的是左括號,則直接將該左括號加入到棧S2中;

(3)如果遇到的是右括號,那麼將棧S2中的運算子一次出棧加入到棧S1中,直到遇到左括號,但是該左括號出棧S2並不加入到棧S1中;

(4)如果遇到的是運算子,包括單目運算子和雙目運算子,我們按照下面的規則進行操作:

          (1)如果此時棧S2為空,則直接將運算子加入到棧S2中;

          (2)如果此時棧S2不為空,當前遍歷的運算子的優先順序大於等於棧頂運算子的優先順序,那麼直接入棧S2;

          (3)如果此時棧S2不為空,當前遍歷的運算子的優先順序小於棧頂運算子的優先順序,則將棧頂運算子一直出棧加入到棧S1中,直到棧為空或者遇到一個運算子的優先順序小於等於當前遍歷的運算子的優先順序,此時將該運算子加入到棧S2中;

(5)直到遍歷完整個中序表示式之後,棧S2中仍然存在運算子,那麼將這些運算子依次出棧加入到棧S1中,直到棧為空。

       按照上面的五條操作反覆進行完成,那麼棧S1中存放的就是逆波蘭表示式。



2、利用逆波蘭表示式求值

       利用逆波蘭表示式求計算式的值其實很簡單,正式因為這一點,所以逆波蘭表示式才在編譯原理中被用於計算一個表示式的值。

       下面來具體看看如何求一個逆波蘭表示式的值:

       我們此時維護一個數據結果棧S3,我們將會看到該棧中最後存放的是最終的表示式的值。我們從左至右的遍歷棧S1,然後按照下面的規則進行操作棧S3.

(1)如果遇到的是數字,那麼直接將數字壓入到S3中;

(2)如果遇到的是單目運算子,那麼取S3棧頂的一個元素進行單目運算之後,將結果再次壓入到棧S3中;

(3)如果遇到的是雙目運算子,那麼取S3棧頂的兩個元素進行,首先出棧的在左,後出棧的在右進行雙目運算子的計算,將結果再次壓入到S3中。

       按照上面的三個規則,遍歷完整個棧S1,那麼最後S3中的值就是逆波蘭表示式的值了,所以我們可以看出來使用逆波蘭表示式進行求值是很簡單的,只有兩種操作要麼是直接壓棧,要麼是運算之後將結果壓棧。
*/