NYOJ-35-表达式求值

Posted f_zyj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NYOJ-35-表达式求值相关的知识,希望对你有一定的参考价值。

描述
ACM队的mdd想做一个计算器,但是,他要做的不仅仅是一计算一个A+B的计算器,他想实现随便输入一个表达式都能求出它的值的计算器,现在请你帮助他来实现这个计算器吧。
比如输入:“1+2/4=”,程序就输出1.50(结果保留两位小数)

输入
第一行输入一个整数n,共有n组测试数据(n<10)。
每组测试数据只有一行,是一个长度不超过1000的字符串,表示这个运算式,每个运算式都是以“=”结束。这个表达式里只包含+-*/与小括号这几种符号。其中小括号可以嵌套使用。数据保证输入的操作数中不会出现负数。
数据保证除数不会为0

输出
每组都输出该组运算式的运算结果,输出结果保留两位小数。

样例输入
2
1.000+2/4=
((1+2)*5+1)/4=

样例输出
1.50
4.00

题目比较容易理解,就是实现四则运算。
说到四则运算,不得不提到逆波兰表示法,也就是后缀表示法。这种表示法不需要括号,对于9 + (3 - 1) x 3 + 10 ÷ 2用后缀表示法的样子则是:9 3 1 - 3 * + 10 2 / +。之所以叫后缀的原因是,所有的运算符号都是要在运算数字的后边出现,那么很容易的想到,我们平时写的式子,也就是第一个式子,是中缀表示法。

中缀表达式转后缀表达式规则:从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即成为后缀表达式的一部分;若是符号,则判断其与栈顶符号的优先级,是右括号或者优先级低于栈顶符号(乘除优先于加减)则栈顶元素依次出栈并输出,并将当前符号进栈,一直到最终输出后缀表达式为止,这里我们需要一个栈来实现,用于符号的进栈出栈。

9 + (3 - 1) x 3 + 10 ÷ 2(中缀表达式) >>> 9 3 1 - 3 * + 10 2 / +(后缀表达式)

后缀表达式使用规则:从左到右遍历后缀表达式的每一个数字和符号,遇到是数字就进栈,遇到是符号,就当处于栈顶的两个数字出栈进行运算,运算结果进栈,一直到最终获得结果。

这里存在两个过程,
1.将中缀表达式转化为后缀表达式(栈用来进出运算符号)。
2.将后缀表达式进行运算得到结果(栈用来进出运算的数字)。

代码如下:

/*
不知道为啥一直WA,可是我试了很多数据都可以的,哎,头疼死了。暂且记下,来日再战,我需要静静。
后边的两个代码均是AC代码。
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define INF 10000000
#define ADD INF + 1 //+
#define SUB INF + 2 //-
#define MUL INF + 3 //x
#define DIV INF + 4 //÷

char str[1005];     //原公式
int len;            //公式长度
char symbol[1000];   //运算符号栈
float suffix[1005]; //后缀式
float answer[1000];  //运算栈

//从s[*pc]开始获取一个浮点数
int StrToInt(char s[], int * pc, float *pout)

    char buf[100];
    int i = 0;

    if(s[*pc]<'0' || s[*pc]>'9')
        return 1;
    else
    
        while((s[*pc] >= '0' && s[*pc] <= '9') || s[*pc] == '.')
        
            buf[i] = s[*pc];
            (*pc)++;
            i++;
        
        buf[i] = '\\0';
        *pout = (float)atof(buf);
        return 0;
    


void swi(int *key, char sym)

    switch (sym)
    
        case '+':
            suffix[(*key)++] = ADD;
            break;
        case '-':
            suffix[(*key)++] = SUB;
            break;
        case '*':
            suffix[(*key)++] = MUL;
            break;
        case '/':
            suffix[(*key)++] = DIV;
            break;
    


void perform(int suf, int *_key)

    float a = answer[(*_key)--];
    float b = answer[(*_key)];
    float c;
    switch (suf)
    
        case (INF + 1):
            c = a + b;
            break;
        case (INF + 2):
            c = b - a;
            break;
        case (INF + 3):
            c = a * b;
            break;
        default:
            c = b / a;
            break;
    
    answer[*_key] = c;
//    printf("%.2f\\n", answer[*_key]);
    return ;


int main()

    int T;
    scanf("%d", &T);

    while(T--)
    
        scanf("%s", str);
        len = (int)strlen(str) - 1;
        int key = 0;
        int top = -1;
        float pout;
        //中缀式转后缀式
        for (int i = 0; i < len; )
        
            if (!StrToInt(str, &i, &pout))
            
                suffix[key++] = pout;
            
            else if (top != -1 && str[i] == ')')
            
                while (symbol[top] != '(')
                
                    swi(&key, symbol[top--]);
                
                i++;
                top--;
            
            else if (top != -1 && (str[i] == '+' || str[i] == '-') && (symbol[top] == '*' || symbol[top] == '/'))
            
                while (symbol[top] != '(' && top >= 0)
                
                    swi(&key, symbol[top--]);
                
                symbol[++top] = str[i++];
            
            else if (top != -1 && (str[i] == '*' || str[i] == '/') && (symbol[top] == '*' || symbol[top] == '/'))
            
                swi(&key, symbol[top]);
                symbol[top] = str[i++];
            
            else
            
                symbol[++top] = str[i++];
            
        
        while (top >= 0)
        
            swi(&key, symbol[top--]);
        

//        for (int i = 0; i < key; i++)
//        
//            printf("%f  ", suffix[i]);
//        
//        printf("\\n");
        //后缀式运算
        int _key = -1;
        for (int i = 0; i < key; i++)
        
            if (suffix[i] < INF)
            
                answer[++_key] = suffix[i];
//                printf("%.2f\\n", answer[_key]);
            
            else
            
                perform((int)suffix[i], &_key);
            
        
        printf("%.2f\\n", answer[0]);
    
    return 0;

另外还有两种方法,第一种是将字符和数据分别入两个栈,然后根据优先级的比较,对数据栈顶的两个元素进行出栈操作然后进栈。

#include<stdio.h>
#include<stdlib.h>

//数据栈
typedef struct DA

    float data[1000];
    int pop;
 SDA;

//运算符栈
typedef struct OP

    char op[1000];
    int pop;
 SOP;

//初始化数据栈
int InitSDA(SDA * p)

    p->pop = 0;
    return 0;


//初始化运算符栈
int InitSOP(SOP * p)

    p->pop = 0;
    (p->op[p->pop]) = '=';
    (p->pop)++;
    return 0;


//数据入栈
int PushSDA(SDA * p, float d)

    if(p->pop < 1000)
    
        p->data[p->pop] = d;
        (p->pop)++;
        return 0;
    
    else
        return 1;   //栈满


//运算符入栈
int PushSOP(SOP * p, char c)

    if(p->pop < 1000)
    
        p->op[p->pop] = c;
        (p->pop)++;
        return 0;
    
    else
        return 1;   //栈满


//数据出栈
int PopSDA(SDA * p, float * d)

    (p->pop)--;
    if(p->pop >= 0)
    
        *d = p->data[p->pop];
        return 0;
    
    else
        return 1;


//运算符出栈
int PopSOP(SOP * p, char * c)

    (p->pop)--;
    if(p->pop >= 0)
    
        *c = p->op[p->pop];
        return 0;
    
    else
        return 1;


//从s[*pc]开始获取一个浮点数
int StrToInt(char s[], int * pc, float *pout)

    char buf[100];
    int i = 0;

    if(s[*pc]<'0' || s[*pc]>'9')
        return 1;
    else
    
        while((s[*pc] >= '0' && s[*pc] <= '9') || s[*pc] == '.')
        
            buf[i] = s[*pc];
            (*pc)++;
            i++;
        
        buf[i] = '\\0';
        *pout = (float)atof(buf);
        return 0;
    


//从s[*pc]获取一个char
int StrToChar(char s[], int *pc, char *pout)

    if('+'==s[*pc] || '-'==s[*pc] || '*'==s[*pc] || '/'==s[*pc] || '('==s[*pc] || ')'==s[*pc])
    
        *pout = s[*pc];
        (*pc)++;
        return 0;
    
    else
        return 1;


//获取优先级
char GetPri(char c1, char c2)


    char f[7][7] = '>', '>', '<', '<', '<', '>', '>',
        '>', '>', '<', '<', '<', '>', '>',
        '>', '>', '>', '>', '<', '>', '>',
        '>', '>', '>', '>', '<', '>', '>',
        '<', '<', '<', '<', '<', '=', '\\0',
        '>', '>', '>', '>', '\\0', '>', '>',
        '<', '<', '<', '<', '<', '\\0', '=',;

    int i=0, j=0;
    switch(c1)
    
        case '+': i = 0; break;
        case '-': i = 1; break;
        case '*': i = 2; break;
        case '/': i = 3; break;
        case '(': i = 4; break;
        case ')': i = 5; break;
        case '=': i = 6; break;
    
    switch(c2)
    
        case '+': j = 0; break;
        case '-': j = 1; break;
        case '*': j = 2; break;
        case '/': j = 3; break;
        case '(': j = 4; break;
        case ')': j = 5; break;
        case '=': j = 6; break;
    
    return f[i][j];


//计算表达式
float Operate(float a, char op, float b)

    switch(op)
    
        case '+': return a + b;
        case '-': return a - b;
        case '*': return a * b;
        case '/': return a / b;
        default: return 0;
    


int main(void)

    char s[10][1000];
    int c = 0;
    float bufda;
    char bufop;
    float a, b;
    SDA sda;
    SOP sop;
    int n;
    int i;


    scanf("%d", &n);
    for(i = 0; i < n; i++)
        scanf("%s", s[i]);
    for(i = 0; i < n; i++)
    
        c = 0;
        InitSDA(&sda);  //初始化数据栈
        InitSOP(&sop);  //初始化符号栈
        while(s[i][c] != '=' || sop.op[sop.pop - 1] != '=') //  计算未完成
        
            if(0 == StrToInt(s[i], &c, &bufda))
                PushSDA(&sda, bufda);   //数据入栈
            else
            
                switch(GetPri(sop.op[sop.pop - 1], s[i][c]))
                
                    case '<':
                        if(0 == StrToChar(s[i], &c, &bufop))
                            PushSOP(&sop, bufop);
                        break;
                    case '=':
                        PopSOP(&sop, &bufop);
                        c++;
                        break;
                    case '>':
                        PopSOP(&sop, &bufop);
                        PopSDA(&sda, &b);
                        PopSDA(&sda, &a);
                        PushSDA(&sda, Operate(a, bufop, b));
                        break;
                
            
        
        PopSDA(&sda, &a);
        printf("%.2f\\n", a);
    
    return 0;



//改写成C++,并简化后如此
//#include <stack>
//#include <stdio.h>
//#include <ctype.h>
//#include <string.h>
//#include <stdlib.h>
//
//using namespace std;
//
//int priority(char c)
//
//    if(c == '=')    return 0;
//    if(c == '+')    return 1;
//    if(c == '-')    return 1;
//    if(c == '*')    return 2;
//    if(c == '/')    return 2;
//    return 0;
//
//
//void compute(stack<double>& Num,stack<char>& Op)
//
//    double b = Num.top();
//    Num.pop();
//    double a = Num.top();
//    Num.pop();
//    switch(Op.top())
//    
//        case '+':Num.push(a+b);break;
//        case '-':Num.push(a-b);break;
//        case '*':Num.push(a*b);break;
//        case '/':Num.push(a/b);break;
//    
//    Op.pop();
//
//
//int main()
//
//    int z;
//    char str[1005];
//    stack<double> Num;
//    stack<char> Op;
//    scanf("%d",&z);
//    while(z--)
//    
//        scanf("%s",str);
//        int len = strlen(str);
//        for(int i=0;i<len;i++)
//        
//            if(isdigit(str[i]))
//            
//                double n = atof(&str[i]);
//                while(i<len && (isdigit(str[i]) || str[i]=='.'))
//                    i++;
//                i--;
//                Num.push(n);
//            
//            else
//            
//                if(str[i] == '(')
//                    Op.push(str[i]);
//                else if(str[i] == ')')
//                
//                    while(Op.top()!='(')
//                        compute(Num,Op);
//                    Op.pop();
//                
//                else if(Op.empty() || priority(str[i])>priority(Op.top()))
//                    Op.push(str[i]);
//                else
//                
//                    while(!Op.empty() && priority(str[i])<=priority(Op.top()))
//                        compute(Num,Op);
//                    Op.push(str[i]);
//                
//            
//        
//        Op.pop();
//        printf("%.2f\\n",Num.top());
//        Num.pop();
//    
//    return 0;
//

第二种是动态规划,将一个大问题切割成些许小问题,和归并排序的思想相仿。

// AC(动态规划)
#include<stdio.h>
#include<string.h>

int len;
int fst[1005];
char str[1005];
double Jud(int begin, int end); /*计算并返回表达式在区间[begin end]中的值*/

int main()

    int T, i;
    double ans;
    scanf("%d", &T);
    while(T--)
    
        memset(fst, 0, sizeof(fst)); /*一定要清0*/
        scanf("%s", str);
        len = (int)strlen(str)-1;
        fst[0] = 1;
        for(i = 1; i <= len - 1; i++) /*fst[i]表示优先级,fst[i]越大,说明优先级越高↓↓*/
                                   /*例如str[] -- ((1+2)*5+1)/4=*/
            if(str[i - 1]== '(')    /*对应fst[] -- 12333222221110*/
                fst[i] = fst[i - 1] + 1;
            else if(str[i] == ')')
                fst[i] = fst[i - 1] - 1;
            else
                fst[i] = fst[i - 1];
        
        ans = Jud(0, len - 1); /*传入整个表达式,不包括=*/
        printf("%.2f\\n", ans);
    
    return 0;


double Jud(int begin, int end)
 /*规定区间[begin, end]的优先级标准为fst[begin]*/
    int i;
    double k;
    for(i = begin; i <= end; i++) /*先从做左到右找到第一个处于指定优先级的'+'运算符*/
    
        if(str[i]== '+' && fst[i] == fst[begin])
        
            k = Jud(begin, i - 1) + Jud(i + 1, end); /*将其拆成两个个表达式的和*/
            return k;
        
    
    for(i = end; i >= begin; i--) /*如果找不到'+',再从右往左找到第一个处于指定优先级的'-'运算符*/
    
        if(str[i]=='-' && fst[i] == fst[begin])
        
            k = Jud(begin, i - 1) - Jud(i + 1, end);    /*将其拆成两个个表达式的差*/
            return k;
        
    
    for(i = begin; i <= end; i++)   /*如果还找不到,再从左往右找到第一个处于指定优先级的'*'运算符*/
    
        if(str[i] == '*' && fst[i] == fst[begin])
        
            k = Jud(begin, i - 1) * Jud(i + 1, end);    /*将其拆成两个个表达式的积*/
            return k;
        
    
    for(i = end; i >= begin; i--)   /*同上,从右往左找到第一个处于指定优先级的'/'运算符*/
    
        if(str[i] == '/' && fst[i] == fst[begin])
        
            k = Jud(begin, i - 1) / Jud(i + 1, end); /*将其拆成两个个表达式的商*/
            return k;
        
    
    if(str[begin]=='(') /*如果在这个[begin,end]区间里的指定优先级中没有任何运算符,说明此区间可能完全包含上一级*/
    
        for(i = begin + 1; fst[i] >= fst[begin + 1]; i++);
        k = Jud(begin + 1, i - 1);
    
    else /*既然没有包含上一级,说明这个区间就只剩下一个数啦*/
    
        char *p = str;
        sscanf(p+begin, "%lf", &k); /*将这个数赋值给k,并返回*/
    
    return k;

理论上这三种均可以AC…可是我不知道第一种哪里出了BUG,等回头有空了再看吧…就这样吧。

以上是关于NYOJ-35-表达式求值的主要内容,如果未能解决你的问题,请参考以下文章

中缀表达式求值

nyoj 35-表达式求值(stack, 栈的应用)

NYOJ-35-表达式求值

表达式求值

NYOJ128 前缀式计算 栈

中缀表达式转换成后缀表达式并求值