【菜鸟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中的值就是逆波兰表达式的值了,所以我们可以看出来使用逆波兰表达式进行求值是很简单的,只有两种操作要么是直接压栈,要么是运算之后将结果压栈。
*/

阅读更多
个人分类: acmer
所属专栏: acmer菜鸟学习工具
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭