表达式求值

一,中缀二元表达式求值

任务描述
本关任务:输入一个中缀算术表达式,求解表达式的值。运算符包括+、-、*、/、(、)、=,参加运算的数为double类型且为正数。(要求:直接针对中缀算术表达式进行计算,不能转换为后缀或前缀表达式再进行计算,只考虑二元运算即可。)

编程要求
输入
多组数据,每组数据一行,对应一个算术表达式,每个表达式均以“=”结尾。当表达式只有一个“=”时,输入结束。参加运算的数为double类型。

输出
对于每组数据输出一行,为表达式的运算结果。输出保留两位小数。

测试说明
平台会对你编写的代码进行测试:

测试输入:
2+2=
20*(4.5-3)=

预期输出:
4.00
30.00

来源
https://www.bjfuacm.com/

思路:
计算机计算中缀(即一般)表达式的原理:
有两个栈,一个倒操作数,一个倒运算符,按输入表达式字符串的顺序分别倒进两个栈,数栈按顺序压入即可,符栈的压入规则是优先级大者才能压入,那么从表达式左边开始让一个个字符入栈时,如果发现有优先级小于刚才压入的符栈元素,停止压入,从符栈取出一个元素,再从数栈取出两个元素(先出来的在右边,后出来的在左边),先计算之,再将这个子表达式的结果入数栈,如果看到括号,将左右阔号及其间的所有内容按上述规则都入栈弹栈计算后再弹出结果压回数栈,如果遇到嵌套括号同样按上述规则处理(注意括号内仍是先乘除后加减),当压到没有东西要压入时,将两个栈弹完计算之,最后返回结果

时间复杂度:0(n)
空间复杂度:0(n)

#include <iostream>
#include<iomanip>
#include <math.h>
#define MAXSIZE 100
#define OK 1
#define ERROR 0
#define OVERFLOW -2
using namespace std;
typedef struct
{//运算符栈
    char *base;
    char *top;
    int stacksize;
}SqStack1;
int InitStack1(SqStack1 &S)
{//运算符栈初始化
    S.base=new char[MAXSIZE];
    if(!S.base) return OVERFLOW;
    S.top=S.base;
    S.stacksize=MAXSIZE;
    return OK;
}
int Push1(SqStack1 &S, char e)
{//运算符栈入栈
    if(S.top-S.base==S.stacksize)  //栈满
        return ERROR;
    *S.top=e;
    S.top++;
    return OK;
}
int Pop1(SqStack1 &S)
{//运算符栈出栈
    if(S.top==S.base)      //栈空
        return ERROR;
    S.top--;
    return OK;
}
char GetTop1(SqStack1 S)
{//运算符栈取栈顶元素
    if(S.top!=S.base)
        return *(S.top-1);
    return ERROR;
}
typedef struct
{//操作数栈
    double *base;
    double *top;
    int  stacksize;
}SqStack2;
int InitStack2(SqStack2 &S)
{//操作数栈初始化
    S.base=new double[MAXSIZE];
    if(!S.base) return OVERFLOW;
    S.top=S.base;
    S.stacksize=MAXSIZE;
    return OK;
}
int Push2(SqStack2 &S,double e)
{//操作数栈入栈
    if(S.top-S.base==S.stacksize)     //栈满
        return ERROR;
    *S.top=e;
    S.top++;
    return OK;
}
int Pop2(SqStack2 &S)
{//操作数栈出栈
    if(S.top==S.base)           //栈空
        return ERROR;
    S.top--;
    return OK;
}
double GetTop2(SqStack2 S)
{//操作数栈取栈顶元素
    if(S.top!=S.base)
        return *(S.top-1);
    return ERROR;
}
double Calculate(double a,char op,double b)
{//计算表达式“a op b”的值
    switch(op)
    {
        case '+':return a+b;
        case '-':return a-b;
        case '*':return a*b;
        case '/':return a/b;
    }
}

char Precede(char a,char b)
{//比较运算符a和b的优先级
    if((a=='('&&b==')')||(a=='='&&b=='='))
        return '=';
    else if(a=='('||a=='='||b=='('||(a=='+'||a=='-')&&(b=='*'||b=='/'))
        return '<';
    else
        return '>';
}

/* 自己加的函数
 * 函数功能:判断遍历到的输入字符串的字符是运算符函数操作数
 * 参数:遍历到的字符
 * 返回值:0表示操作数,1表示运算符*/
static inline int n_OR_c(char e) {
    if (e == '+' || e == '-' || e == '*' || e == '/' || e == '=' || e == '(' || e == ')')
        return 1;
    else
        return 0;
}

/*自己加的函数
 * 函数功能:从两个栈中分别取出(弹出)两个操作数和一个运算符进行一个二元运算,最后将计算结果压回数栈
 * 参数:符栈和数栈的地址
 * 返回值:无*/
static void TWOCalculate(SqStack1 & OPTR,SqStack2 & OPND) {
    double b = GetTop2(OPND); Pop2(OPND); double a = GetTop2(OPND); Pop2(OPND);
    Push2( OPND, Calculate(a, GetTop1(OPTR), b) );  Pop1(OPTR);
}

/* 自己加的函数
 * 功能:将遍历到的(连续的)数字字符转换为十进制数字,并更新输入字符串的遍历的索引i值
 * 参数:优先级算法函数中正在遍历输入字符串的索引值i,输入字符串的副本
 * 返回值:十进制换算结果
 * */
static double Decimalism(int &i, char s[]) {
        double k = 0;
        while (! n_OR_c(s[i])) {
            k = k*10 + s[i++] - '0';
            if(s[i] == '.') {
                ++i;
                int j = 1;      // 小数的权重
                while (! n_OR_c(s[i]))
                    k = k + (s[i] - '0') * 1/pow(10,j++), ++i;
            }
        }
        return k;
}

double EvaluateExpression(SqStack1 OPTR,SqStack2 OPND,char s[])
{//算术表达式求值的算符优先算法
    /**************begin************/

    int i = 0;
    while (s[i] != '=')       // =是输入的表达式的末尾符号
    {
            // 如果看到左括号先计算左括号直到右括号间的内容,左括号入栈作为括号内表达式的结束标志,右括号不入栈
            if (s[i] == '(')
            {
                int cnt1 = 0; int cnt2 = 0;           // cnt1表示括号内(不算括号)的运算符数量,cnt2表示操作数数量
                Push1(OPTR, s[i++]);
                // ----------------处理括号内还有括号的嵌套括号情况-----------------
                if (s[i] == '(')
                {
                    Push1(OPTR, s[i++]);
                    int cnt01 = 0; int cnt02 = 0;           // cnt01表示括号内(不算括号)的运算符数量,cnt02表示操作数数量
                    while (s[i] != ')')
                    {
                        // 压入括号内操作数
                        if (! n_OR_c(s[i])) {
                            Push2(OPND, Decimalism(i, s) );
                            cnt02++;
                        }
                        // 处理括号内有乘除法的特殊情况
                        else if (cnt01 > 0 && cnt02 > 1 && Precede(s[i], *(OPTR.top-1) ) == '<'){
                            TWOCalculate(OPTR, OPND);
                            cnt01 -= 1, cnt02 -= 2;
                        }
                        // 压入括号内运算符
                        else {
                            Push1(OPTR, s[i++]);
                            cnt01++;
                        }
                    }
                    // 上面已经处理了括号内先乘除,后加减的特殊情况,现在计算完两个栈中存的剩余的括号内的式子
                    while (*(OPTR.top - 1) != '(') {
                        TWOCalculate(OPTR, OPND);
                    }  Pop1(OPTR);        // 弹掉左括号
                    i++;           // 跳过右括号
                    cnt2++;
                }
                                // ----------------处理括号内还有括号的嵌套括号情况-----------------

                while (s[i] != ')')
                {
                    // 压入括号内操作数
                    if (! n_OR_c(s[i])) {
                        Push2(OPND, Decimalism(i, s) );
                        cnt2++;
                    }
                    // 处理括号内有乘除法的特殊情况
                    else if (cnt1 > 0 && cnt2 > 1 && Precede(s[i], *(OPTR.top-1) ) == '<'){
                        TWOCalculate(OPTR, OPND);
                        cnt1 -= 1, cnt2 -= 2;
                    }
                    // 压入括号内运算符
                    else {
                        Push1(OPTR, s[i++]);
                        cnt1++;
                    }
                }
                // 上面已经处理了括号内先乘除,后加减的特殊情况,现在计算完两个栈中存的剩余的括号内的式子
                while (*(OPTR.top - 1) != '(') {
                    TWOCalculate(OPTR, OPND);
                }  Pop1(OPTR);        // 弹掉左括号
                i++;           // 跳过右括号
            }
            // 剩余的就是括号外的情况,先优先计算乘除法
            else if (n_OR_c(s[i]) && Precede( s[i], GetTop1(OPTR) ) == '<')
            {
                TWOCalculate(OPTR, OPND);
            }
            // 其它的正常压入
            else
            {
                n_OR_c(s[i]) ? Push1(OPTR, s[i++]) : Push2(OPND, Decimalism(i, s));
            }
    }
    // 压完并且计算完其中部分乘除法和括号内的式子,现在计算完两个栈内存的剩余式子,注意上面并没有压入‘=’,所以这里不用担心
    while ( *(OPTR.top-1) != '=')
    {
        TWOCalculate(OPTR, OPND);
    }
    double rel = GetTop2(OPND); Pop2(OPND);

    return rel;

    /**************end************/
}


int main()
{//设OPTR和OPND分别为运算符栈和操作数栈
    SqStack1 OPTR;
    InitStack1(OPTR);    //初始化运算符栈
    SqStack2 OPND;
    InitStack2(OPND);    //初始化操作数栈
    Push1(OPTR,'=');     //将表达式起始符“=”压入OPTR栈
    char s[100];
    while(cin>>s)
    {//循环读入多组数据
        if(s[0]=='=')
            break;    //当表达式只有一个“=”时,输入结束
        //输出中缀算术表达式的值
        cout<<fixed<<setprecision(2)<<EvaluateExpression(OPTR,OPND,s)<<fixed<<setprecision(2)<<endl;

    }
    return 0;
}

二,波兰表达式求值

编程要求

波兰表达式是一种把运算符前置的算术表达式,如:

2 + 3的逆波兰表示法为+ 2 3。

(2 + 3) * 4的逆波兰表示法为* + 2 3 4。

现在请你编写一个程序,读取一段正确的逆波兰表达式,计算并输出它的结果。

输入的表达式中只有+ - * /四种运算符,且数字均为1位,但是运算时要按照浮点数float来运算。

请在右侧编辑器的Cal函数中,读取输入的逆波兰表达式,计算并输出结果,占一行。

输入数据需要学员自己读取。具体见测试说明。

测试说明
平台会对你编写的代码进行测试:

预期输入:+ 2 3
预期输出:5

预期输入:* + 2 3 4
预期输出:20

思路:使用递归进行自动分析

  • 首先假设我们的递归函数能够计算出一个子表达式
  • 那么就分类递出
  • 最后设置归回条件

时间复杂度:
空间复杂度:O(1)

#include <iostream>  
using namespace std;  
/**********   Begin   **********/  

/* 前缀表达式求值 */  
float eval(char * s, int &i) {  
    // 过掉空格  
    while (s[i] == ' ') {  
        i++;  
    }
    if (s[i] == '+') {  
        i++;  
        return eval(s, i) + eval(s, i);  
    }
    else if (s[i] == '*') {  
        i++;  
        return eval(s, i) * eval(s, i);  
    }
    else if (s[i] == '-') {  
        i++;  
        return eval(s, i) - eval(s, i);  
    }
    else if (s[i] == '/') {
        i++;
        return eval(s, i) / eval(s, i);
    }
    // 将操作数转换为十进制
    else
        return s[i++] - '0';
}
void Cal()
{
    char input[25];
    int i = 0;
    fgets(input, 25, stdin);
    printf("%0.f\n", eval(input, i));
}
/**********   End   **********/


三,逆波兰表达式求值

You are given an array of strings tokens that represents an arithmetic expression in a Reverse Polish Notation.

Evaluate the expression. Return an integer that represents the value of the expression.

Note that:

The valid operators are ‘+’, ‘-’, ‘*’, and ‘/’.
Each operand may be an integer or another expression.
The division between two integers always truncates toward zero.
There will not be any division by zero.
The input represents a valid arithmetic expression in a reverse polish notation.
The answer and all the intermediate calculations can be represented in a 32-bit integer.

Example 1:

Input: tokens = [“2”,“1”,“+”,“3”,“*”]
Output: 9
Explanation: ((2 + 1) * 3) = 9
Example 2:

Input: tokens = [“4”,“13”,“5”,“/”,“+”]
Output: 6
Explanation: (4 + (13 / 5)) = 6
Example 3:

Input: tokens = [“10”,“6”,“9”,“3”,“+”,“-11”,““,”/“,””,“17”,“+”,“5”,“+”]

Output: 22
Explanation: ((10 * (6 / ((9 + 3) * -11))) + 17) + 5

= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22

Constraints:

1 <= tokens.length <= 104
tokens[i] is either an operator: “+”, “-”, “*”, or “/”, or an integer in the range [-200, 200].

来源:https://leetcode.cn/problems/evaluate-reverse-polish-notation/?envType=problem-list-v2&envId=9Rkf2tiI

思路:
设置一个栈,遍历输入的字符串,操作数入之,遇到运算符时拿出栈中两个元素与其进行运算,再将结果压回栈中,直到遍历遍历完毕,输出栈中最后一个元素。

注意点:如果需要比较=或者 != 的字符串数组里的元素等不等于一个普通的单个的字符,最
好把它赋给一个普通一维字符数组,还要注意如果要判断一个字符串元素是否为数
字,要考虑-号可能会在判断时被认定为减:

时间复杂度:0(n)
空间复杂度:0(n)

/* 自己加的函数
* 函数功能:将数字字符转换为十进制数字
* 参数:数字字符
* 返回值:十进制整数 */
static long convert(char* c) {
    long rel = 0, i = 0, flag = 1;
    if (c[i] == '-')
        i++, flag = -1;
    while (c[i] != '\0') {
        rel = rel * 10 + (c[i++] - '0');
    }
    return rel * flag;
}

/* 自己加的函数
* 函数功能:计算一个二元子表达式
* 参数:a,b依次是栈顶数字,c是运算符
* 返回值:整数 */
static long calculate(long a, char c, long b) {
    switch (c)
    {
        case '+':
            return a + b;
        case '-':
            return a - b;
        case '*':
            return a * b;
        case '/':
            return a / b;
    }
    return 0;
}

int evalRPN(char** tokens, int tokensSize) {
    long stack[tokensSize], top = -1;
    for (long i = 0; i < tokensSize; i++)
    {
        char* token = tokens[i];
        if (strlen(token) > 1 || ('0' <= token[0] && token[0] <= '9')) {
            stack[++top] = convert(token);
        }
        else {
            long b = stack[top--];
            long a = stack[top--];
            long tmp = calculate(a, token[0], b);
            stack[++top] = tmp;
        }
    }
    return stack[top--];
}```


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值