中缀表达式问题

例题:ACWING151. 表达式计算4(表达式解析)

  • PS:3月的CCF第二题是解析表达式算24点,解析表达式毫无疑问用的是栈。不过真有某些仙人直接64个for暴力枚举表达式。。。

  • 将一个普通的中序表达式转换为逆波兰表达式的一般算法是:
    首先需要分配2个栈,一个作为临时存储运算符的栈S1(含一个结束符号),一个作为输入逆波兰式的栈S2(空栈),S1栈可先放入优先级最低的运算符#,注意,中缀式应以此最低优先级的运算符结束。可指定其他字符,不一定非#不可。从中缀式的左端开始取字符,逐序进行如下步骤:
    (1)若取出的字符是操作数,则分析出完整的运算数,该操作数直接送入S2栈
    (2)若取出的字符是运算符,则将该运算符与S1栈栈顶元素比较,如果该运算符优先级(不包括括号运算符)大于S1栈栈顶运算符优先级,则将该运算符进S1栈,否则,将S1栈的栈顶运算符弹出,送入S2栈中,直至S1栈栈顶运算符低于(不包括等于)该运算符优先级,最后将该运算符送入S1栈。
    (3)若取出的字符是“(”,则直接送入S1栈顶。
    (4)若取出的字符是“)”,则将距离S1栈栈顶最近的“(”之间的运算符,逐个出栈,依次送入S2栈,此时抛弃“(”。
    (5)重复上面的1~4步,直至处理完所有的输入字符
    (6)若取出的字符是“#”,则将S1栈内所有运算符(不包括“#”),逐个出栈,依次送入S2栈。
    完成以上步骤,S2栈便为逆波兰式输出结果。不过S2应做一下逆序处理。便可以按照逆波兰式的计算方法计算了!

新建一个表达式,如果当前字符为变量或者为数字,则压栈,如果是运算符,则将栈顶两个元素弹出作相应运算,结果再入栈,最后当表达式扫描完后,栈里的就是结果。

  • 换句话说中缀表达式转后缀表达式,其过程有三种考虑的东西(表达式,表达数,括号),具体的过程就是表达数进队列,表达式进栈(如果栈顶表达式的优先级大于该表达式,则栈顶表达式进队列,保证了运算的优先级。至于为什么保证了运算的优先级,我们就要回到运算式子本身,运算式中,表达式优先级高的运算符先发挥作用,将其运算符进栈,运算数进队列以后,如果后面遇到的运算符优先级较低但是将其进栈会导致之后的计算中这两个数会与运算优先级低的这个运算符结合,导致错误)。
    逆波兰表达式就好办了,无非就是进队列和出队列(进运算数,遇到运算符出两个数并且后出来的那个数先结合运算符(栈的先进后出))。

  • 但是我们还能进一步探讨这个运算符计算的问题,这个运算符的本质是用栈与队列实现的一种运算过程,其中栈是主导,队列只是一个缓冲过程。栈的数据结构一样是先进后出,递归的本质是层次操作,本质上也是栈来实现的。那么中缀表达式本身就可以用递归实现,具体过程类似上面一段的分析,将表达式分为三个部分,运算符,运算数,括号,遇到运算数则转移到运算符,遇到运算符则转移到运算数,遇到左括号转移到运算数,遇到右括号转移到运算数。互相调用,层次推进。类似以前我问过老师你的题目——递归逆序输出数组元素,其实也可以递归正序输出数组。只需要将输出语句分别放在递归语句后面与前面就可以了,代码很简单,不实现了。本质上是参差不同导致的差异性结果。表达式无非就是有了两种元素——运算符运算数(括号仅仅是辅助作用),每个运算符控制两个运算数,两个获得运算数递归语句,一个获得运算符并输出的语句,可以有三种不同的递归层次,这样就可以构成一颗表达式树,之后就可以进行经典的表达式树输出(按照遍历次序的不同构成不同的表达式)

#include <cstdio>
#include <map>
#include <queue>
#include <stack>
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;

struct node
{
    int num;//数字
    char op;//操作符
    bool flag;//true表示操作数,false表示操作符。
};

//map<string, int> p;
map<char, int> p;//存储运算优先级

stack<node> s;//操作符
stack<node> s1;//数字,用于计算
queue<node> q;//存储后缀表达式

void trans(string str)//中缀转后缀,后缀好计算。
{
    node temp;//中转存储
    for(int i = 0;i < str.length();)//遍历算式
    {
        if(str[i] == '(')//左括号
        {
            temp.flag = false;//操作符
            temp.op = str[i];
            s.push(temp);//入栈
            i++;
        }
        else if(str[i] == ')')
        {
            while(!s.empty()&&s.top().op != '(')//遇到右括号则令其出栈至后缀表达式队列
            {
                q.push(s.top());//入队
                s.pop();//出栈
            }
            s.pop();//左括号出栈
            i++;
        }
        else  if(str[i] >= '0' && str[i] <= '9')//数字
        {
            temp.flag = true;//操作数
            temp.num = str[i] - '0';
            i++;
            while(i < str.length()&&str[i] >= '0' && str[i] <= '9')//由于输入的是字符,需要进行多位数字处理。
            {
                temp.num = temp.num * 10 + (str[i] - '0');
                i++;
            }
            q.push(temp);//数字直接入队
        }
        else
        {
            temp.flag = false;//运算符
            while(!s.empty()&&p[s.top().op] >= p[str[i]])//栈不为空且栈顶优先级大于该运算符
            {//保证高优先级的运算符先运算
                q.push(s.top());//入队
                s.pop();
            }
            temp.op = str[i];//
            s.push(temp);
            i++;
        }
    }

    while(!s.empty())//栈中内容输入队列
    {
        q.push(s.top());
        s.pop();
    }

}

int calculate()
{
    double num1,num2;//数字1,数字2,运算符用来计算前两位数字
    node cur,temp;
    while(!q.empty())
    {
        cur = q.front();//队首元素
        q.pop();
        if(cur.flag == true)//操作数进栈
        {
            s1.push(cur);
        }
        else
        {
            num2 = s1.top().num;
            s1.pop();
            num1 = s1.top().num;
            s1.pop();
            temp.flag = true;
            if(cur.op == '+')//获得运算符与运算数,执行运算。
            {
                temp.num = num1 + num2;
            }
            else if(cur.op == '-')
            {
                temp.num = num1 - num2;
            }
            else if(cur.op == '*')
            {
                temp.num = num1 * num2;
            }
            else if(cur.op == '+')
            {
                temp.num = num1 + num2;
            }
            else if(cur. op == '/')
            {
                temp.num = num1 + num2;
            }
            s1.push(temp);
        }
    }
    return s1.top().num;
}

int main()
{
    p['+'] = 1;
    p['-'] = 1;//赋给运算符优先级
    p['*'] = 2;
    p['/'] = 2;

    string str;//输入的运算表达式
    cin>>str;
    trans(str);//中缀转后缀

    double ans = calculate();

    cout<<ans<<endl;
}
//后缀表达式
#include<iostream>//后缀表达式递归解法。
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
string a[2000];
int i,t;
int f1(int a,char b,int c)
{
    switch(b)
    {
        case'+':return a+c;
        case'-':return a-c;
        case'*':return a*c;
        case'/':return a/c;
    }
}
int f2()
{
    int i=t;
    if(a[i][0]>='0'&&a[i][0]<='9')
    {
        t--;
        return atof(a[i].c_str());
    }
    t--;
    return f1(f2(),a[i][0],f2());
}

int main()
{
    string b;
    while(cin>>b&&b!="#")
        a[++i]=b;
    t=i;
    printf("%d",f2());

    return 0;
}
//前缀表达式

#include <cstdio>
#include <cstdlib>

using namespace std;

int exp()//前缀表达式
{
    char str[1005];
    scanf("%s",str);
    switch(str[0])
    {
        case'+':return exp() + exp();
        case'-':return exp() - exp();
        case'*':return exp() * exp();
        case'/':return exp() / exp();
        default:return atof(str);
    }
}


int main()
{
    double ans;
    ans = exp();
    printf("%lf\n",ans);
    return 0;
}
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:设计师小姐姐 返回首页