HDU 1237 表达式求值

最近数据结构作业有一道表达式求值的题,在网上学习了一下。

对于表达式的记法,有后缀表达式(逆波兰式),前缀表达式(波兰式0和中缀表达式。而这些定义是操作数和操作符的前后位置定义的。

前缀表达式:操作符位于操作数之前。

中缀表达式:操作符位于操作数之中。

后缀表达式:操作符位于操作数之后。

对于中缀表达式,我们利用括号来改变优先级关系。

但是:对于前缀和后缀表达式,我们通过操作符的位置来改变优先级关系。换句话说,在前缀和后缀表达式中是没有括号的。

在日常生活中,我们用到的是中缀表达式。但是,在中缀表达式中我们为了处理优先级的关系,是用括号来提高优先级,这对计算机分析表达式并求值造成了不便。

当然,可以认为中缀表达式中的括号是递归的,那我们就能用递归的方式来写算法。

我们今天要讨论一下前缀和后缀表达式,因为,我们可以利用栈这个数据结构,来高效的对前缀和后缀表达式求和。


中缀表达式转后缀表达式,我们有以下的算法:

需要注意一点的是:在后缀表达式中,运算符的越靠后,优先级越低
(1) 初始化两个栈:运算符栈S1和储存中间结果的栈S2;
(2) 从左至右扫描中缀表达式;
(3) 遇到操作数时,将其压入S2;
(4) 遇到运算符时,比较其与S1栈顶运算符的优先级:
(4-1) 如果S1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
(4-2) 否则,若优先级比栈顶运算符的高,也将运算符压入S1;
(4-3) 否则,将S1栈顶的运算符弹出并压入到S2中,再次转到(4-1)与S1中新的栈顶运算符相比较;

(注意:这里需要注意的是当前考虑运算符的左右结合性。如果是左结合,那对于相等优先级,先出现的运算符将先进行运算,即将先前的运算符弹出。如果是右结合,那对于相等优先级,后出现的运算符将先运算,所以我们不能弹出先前的运算符)
(5) 遇到括号时:
(5-1) 如果是左括号“(”,则直接压入S1;
(5-2) 如果是右括号“)”,则依次弹出S1栈顶的运算符,并压入S2,直到遇到左括号为止,此时将这一对括号丢弃;
(6) 重复步骤(2)至(5),直到表达式的最右边;
(7) 将S1中剩余的运算符依次弹出并压入S2;
(8) 依次弹出S2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式。

而对于后缀表达式,我们有以下算法计算表达式的值:

1.如果为操作数,将其压入栈。

2.如果为操作符,为双目运算符,将两个元素从栈弹出,计算结果,再压入栈。如果为单目运算符,将一个元素弹出栈,计算结果,在将其入栈。


整个的代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cctype>
#include <cstdio>
#include <algorithm>
#include <cmath>

using namespace std;

struct unit;
int top1,top2;
int trans(char*s);
int trans(char s);
int prio(int op);
void toPolishNotation(char * expr);
double calc(double r, int op);
double calc(double r1,double r2, int op);
double calc_expr(void);

int main(void)
{
    freopen("input.txt","r",stdin);
    char expr[1000];
    cin>>expr;
    toPolishNotation(expr);
    cout<<calc_expr();
    return 0;
}


struct unit{
    int type;//0表示不是数据,1表示是数据
    double data;
    int op;
    /*
    操作符类型
    0 (     1 )
    2 +     3 -
    4 *     5 /
    6 ^     7 sin
    8 cos   9 log
    10 ln */

    unit(){}
    unit(int t, int o, double d):type(t),op(o),data(d){}

} stack1[200],stack2[200];
/*stack1 存放最后结果, stack2存放中间结果*/

void toPolishNotation(char * expr)
{
    top1 = top2 = 0;
    for(int i = 0; expr[i] != '='; ){
        if(isdigit(expr[i])){
            char number[40];
            int j = 0;
            while(isdigit(expr[i]) || expr[i] == '.')
                number[j++] = expr[i++];
            number[j] = 0;
            double data;
            sscanf(number,"%lf",&data);
            stack1[top1++] = unit(1,0,data);
        }
        else{
            int op;
            if(isalpha(expr[i])){
                char fun[4];
                int j = 0;
                while(isalpha(expr[i]))
                    fun[j++] = expr[i++];
                fun[j] = 0;
                op = trans(fun);
            }
            else
                op = trans(expr[i++]);
            if(op == 1){//右括号
                while(stack2[top2-1].op != 0)
                        stack1[top1++] = stack2[--top2];
                --top2;
            }
            else if(op == 0)//左括号
                stack2[top2++] = unit(0,op,0);
            else{//将优先级大于等于当前运算符的运算符加入后缀表达式
                while(top2 > 0 && prio(stack2[top2-1].op) > prio(op))
                    stack1[top1++] = stack2[--top2];
                stack2[top2++] = unit(0,op,0);
            }
        }
    }
    while(top2 > 0){
        stack1[top1++] = stack2[--top2];
    }
}

double calc_expr(void)
{
    top2 = 0;
    for(int i = 0 ; i < top1; ++i){
        if(stack1[i].type)
            stack2[top2++] = stack1[i];
        else{
            if(stack1[i].op >= 7){
                double r1 = stack2[top2-1].data;
                double r2 = calc(r1,stack1[i].op);
                stack2[top2-1].data = r2;
            }
            else{
                double r1 = stack2[--top2].data;
                double r2 = stack2[top2-1].data;
                double r3 = calc(r2,r1,stack1[i].op);
                stack2[top2-1].data = r3;
            }
        }
    }
    return stack2[0].data;
}

int prio(int op)
{
    if(op == 0 || op == 1)
        return 0;
    else if(op == 2 || op == 3)
        return 1;
    else if(op == 4 || op == 5 || op == 6)
        return 2;
    else
        return 3;
}


int trans(char *s)
{
    if(strcmp(s,"sin") == 0)
        return 7;
    else if(strcmp(s,"cos") == 0)
        return 8;
    else if(strcmp(s,"log") == 0)
        return 9;
    else if(strcmp(s,"ln") == 0)
        return 10;
}
int trans(char s)
{
    switch(s){
    case '(':
        return 0;
    case ')':
        return 1;
    case '+':
        return 2;
    case '-':
        return 3;
    case '*':
        return 4;
    case '/':
        return 5;
    case '^':
        return 6;
    }
}

double calc(double r, int op)
{
    switch(op){
    case 7:
        return sin(r);
    case 8:
        return cos(r);
    case 9:
        return log10(r);
    case 10:
        return log(r);
    }
}

double calc(double r1,double r2, int op)
{
    switch(op){
    case 2:
        return r1 + r2;
    case 3:
        return r1 - r2;
    case 4:
        return r1 * r2;
    case 5:
        return r1 / r2;
    case 6:
        return pow(r1,r2);
    }
}



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值