4.2 求值算法 RPN逆波兰表达式

求值算法,来自邓俊辉老师的数据结构
基于Stack的类模板,处理中缀表达式求值,和将中缀表达式转转换成后缀表达式

#include "Stack.h"//基于之前写的类模板Vector
#include<cstdio>
#include<ctype.h>
#include<cstring>
#include<cmath>
#include"Stack.h"

using namespace std;

#define N_OPTR 9 //运算符总数
typedef enum
{
    ADD, SUB, MUL, DIV, POW, FAC, L_P, R_P, EOE,
} Operator; //运算符集合


//加、减、乘、除、乘方、阶乘、左括号、右括号、起始符与终止符
const char pri[N_OPTR][N_OPTR] =
{ //运算符优先等级 [栈顶] [当前]
   /*              |-------------------- 当 前 运 算 符 --------------------| */
   /*              +      -      *      /      ^      !      (      )      \0 */
    /* --  + */    '>',   '>',   '<',   '<',   '<',   '<',   '<',   '>',   '>',
    /* |   - */    '>',   '>',   '<',   '<',   '<',   '<',   '<',   '>',   '>',
    /* 栈  * */    '>',   '>',   '>',   '>',   '<',   '<',   '<',   '>',   '>',
    /* 顶  / */    '>',   '>',   '>',   '>',   '<',   '<',   '<',   '>',   '>',
    /* 运  ^ */    '>',   '>',   '>',   '>',   '>',   '<',   '<',   '>',   '>',
    /* 算  ! */    '>',   '>',   '>',   '>',   '>',   '>',   ' ',   '>',   '>',
    /* 符  ( */    '<',   '<',   '<',   '<',   '<',   '<',   '<',   '=',   ' ',
    /* |   ) */    ' ',   ' ',   ' ',   ' ',   ' ',   ' ',   ' ',   ' ',   ' ',
    /* -- \0 */    '<',   '<',   '<',   '<',   '<',   '<',   '<',   ' ',   '='
};

void readNumber(char*& p, Stack<double>& stk)
{ //将起始于p的子串解析为数值,并存入操作数栈
    stk.push((double)(*p - '0')); //当前数位对应的数值进栈
    while (isdigit(*(++p))) //只要后续还有紧邻的数字(即多位整数的情况),则
    {
        stk.push(stk.pop() * 10 + (*p - '0')); //弹出原操作数并追加新数位后,新数值重新入栈
    }
    if ('.' != *p)
    {
        return; //此后非小数点,则意味着当前操作数解析完成
    }
    float fraction = 1; //否则,意味着还有小数部分
    while (isdigit(*(++p))) //逐位加入
    {
        stk.push(stk.pop() + (*p - '0') * (fraction /= 10)); //小数部分
    }
}

void append(char*& rpn, double opnd)
{ //将操作数接至RPN末尾
    char buf[64];
    if (0.0 < opnd - (int)opnd)
    {
        sprintf(buf, "%f \0", opnd); //浮点格式,或
    }
    else
    {
        sprintf(buf, "%d \0", (int)opnd); //整数格式
    }
    rpn = (char*)realloc(rpn, sizeof(char) * (strlen(rpn) + strlen(buf) + 1)); //扩展空间
    strcat(rpn, buf); //RPN加长
}

void append(char*& rpn, char optr)
{ //将运算符接至RPN末尾
    int n = strlen(rpn); //RPN当前长度(以'\0'结尾,长度n + 1)
    rpn = (char*)realloc(rpn, sizeof(char) * (n + 3)); //扩展空间
    sprintf(rpn + n, "%c ", optr);
    rpn[n + 2] = '\0'; //接入指定的运算符
}


Operator optr2rank(char op)
{ //由运算符转译出编号
    switch (op)
    {
        case '+':
        {
            return ADD; //加
        }
        case '-':
        {
            return SUB; //减
        }
        case '*':
        {
            return MUL; //乘
        }
        case '/':
        {
            return DIV; //除
        }
        case '^':
        {
            return POW; //乘方
        }
        case '!':
        {
            return FAC; //阶乘
        }
        case '(':
        {
            return L_P; //左括号
        }
        case ')':
        {
            return R_P; //右括号
        }
        case '\0':
        {
            return EOE; //起始符与终止符
        }
        default:
        {
            exit(-1); //未知运算符
        }
    }
}

char orderBetween(char op1, char op2) //比较两个运算符之间的优先级
{
    return pri[optr2rank(op1)][optr2rank(op2)];
}

__int64 facI(int n)
{
    __int64 f = 1;
    while (n > 1)
    {
        f *= n--;
    }
    return f;
} //阶乘运算(迭代版)

double calcu(double a, char op, double b)
{ //执行二元运算
    switch (op)
    {
        case '+':
        {
            return a + b;
        }
        case '-':
        {
            return a - b;
        }
        case '*':
        {
            return a * b;
        }
        case '/':
        {
            if (0 == b)
            {
                exit(-1);
            }
            else
            {
                return a / b; //注意:如此判浮点数为零可能不安全
            }
        }
        case '^':
        {
            return pow(a, b);
        }
        default:
        {
            exit(-1);
        }
    }
}

double calcu(char op, double b)
{ //执行一元运算
    switch (op)
    {
        case '!':
        {
            return (double)facI((int)b); //目前仅有阶乘,可照此方式添加
        }
        default:
        {
            exit(-1);
        }
    }
}


double evaluate(char* S, char*& RPN)
{ //对(已剔除白空格的)表达式S求值,并转换为逆波兰式RPN
    Stack<double> opnd;
    Stack<char> optr; //运算数栈、运算符栈 /*DSA*/任何时刻,其中每对相邻元素之间均大小一致
    char* expr = S;
    optr.push('\0'); //尾哨兵'\0'也作为头哨兵首先入栈
    while (!optr.empty())
    { //在运算符栈非空之前,逐个处理表达式中各字符
        if (isdigit(*S))
        { //若当前字符为操作数,则
            readNumber(S, opnd);
            append(RPN, opnd.top()); //读入操作数,并将其接至RPN末尾
        }
        else //若当前字符为运算符,则
        {
            switch (orderBetween(optr.top(), *S))
            { //视其与栈顶运算符之间优先级高低分别处理
                case '<': //栈顶运算符优先级更低时
                {
                    optr.push(*S); S++; //计算推迟,当前运算符进栈
                    break;
                }
                case '=': //优先级相等(当前运算符为右括号或者尾部哨兵'\0')时
                {
                    optr.pop(); S++; //脱括号并接收下一个字符
                    break;
                }
                case '>':
                { //栈顶运算符优先级更高时,可实施相应的计算,并将结果重新入栈
                    char op = optr.pop(); append(RPN, op); //栈顶运算符出栈并续接至RPN末尾
                    if ('!' == op)
                    { //若属于一元运算符
                        double pOpnd = opnd.pop(); //只需取出一个操作数,并
                        opnd.push(calcu(op, pOpnd)); //实施一元计算,结果入栈
                    }
                    else
                    { //对于其它(二元)运算符
                        double pOpnd2 = opnd.pop(), pOpnd1 = opnd.pop(); //取出后、前操作数 /*DSA*/提问:
                                                                        //可否省去两个临时变量?
                        opnd.push(calcu(pOpnd1, op, pOpnd2)); //实施二元计算,结果入栈
                    }
                    break;
                }
                default:
                {
                    exit(-1); //逢语法错误,不做处理直接退出
                }
            }

        }
    }
    return opnd.pop(); //弹出并返回最后的计算结果
}


char* removeSpace ( char* s )
{ //剔除s[]中的白空格
   char* p = s, *q = s;
   while ( true )
   {
       while (isspace(*q))
       {
           q++;
       }
      if ( '\0' == *q )
      {
          *p = '\0';
          return s;
      }
      *p++ = *q++;
   }
}


//教材实例代码包中的测试用例
//值得注意的是这里所使用的测试用例需要在VS2019中点击属性然后手动设置命令参数


/*
int main(int argc, char* argv[])
{ //表达式求值(入口)

    for (int i = 1; i < argc; i++)
    { //逐一处理各命令行参数(表达式)
        system("cls");
        printf("\nPress any key to evaluate: [%s]\a\n", argv[i]);
        getchar();
        char* rpn = (char*)malloc(sizeof(char) * 1);
        rpn[0] = '\0'; //逆波兰表达式
        double value = evaluate(removeSpace(argv[i]), rpn); //求值
        printf("EXPR\t: %s\n", argv[i]); //输出原表达式
        printf("RPN\t: [ %s]\n", rpn); //输出RPN
        printf("Value\t= %f = %d\n", value, (int)value); //输出表达式的值
        free(rpn);
        rpn = NULL;
        getchar();
    }


    system("pause");
    return 0;
}
*/
int main()
{
    char* rpn = (char*)malloc(sizeof(char) * 1);
    rpn[0] = '\0'; //逆波兰表达式
    double value = evaluate(const_cast < char*>("(1+2^3!-4)*(5!-(6-(7-(89-0!))))"), rpn); //求值
    cout << value << endl;
    cout << "the rpn is:" << rpn;
    free(rpn);
    rpn = NULL;

    system("pause");
    return 0;
}

不基于手打的Stack类模板,而是基于已有的库函数实现求值算法
处理中缀表达式求值,和将中缀表达式转转换成后缀表达式

#include<stack>
#include<cstdio>
#include<ctype.h>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
using namespace std;

#define N_OPTR 9 //运算符总数
typedef enum
{
    ADD, SUB, MUL, DIV, POW, FAC, L_P, R_P, EOE,
} Operator; //运算符集合


//加、减、乘、除、乘方、阶乘、左括号、右括号、起始符与终止符
const char pri[N_OPTR][N_OPTR] =
{ //运算符优先等级 [栈顶] [当前]
   /*              |-------------------- 当 前 运 算 符 --------------------| */
   /*              +      -      *      /      ^      !      (      )      \0 */
    /* --  + */    '>',   '>',   '<',   '<',   '<',   '<',   '<',   '>',   '>',
    /* |   - */    '>',   '>',   '<',   '<',   '<',   '<',   '<',   '>',   '>',
    /* 栈  * */    '>',   '>',   '>',   '>',   '<',   '<',   '<',   '>',   '>',
    /* 顶  / */    '>',   '>',   '>',   '>',   '<',   '<',   '<',   '>',   '>',
    /* 运  ^ */    '>',   '>',   '>',   '>',   '>',   '<',   '<',   '>',   '>',
    /* 算  ! */    '>',   '>',   '>',   '>',   '>',   '>',   ' ',   '>',   '>',
    /* 符  ( */    '<',   '<',   '<',   '<',   '<',   '<',   '<',   '=',   ' ',
    /* |   ) */    ' ',   ' ',   ' ',   ' ',   ' ',   ' ',   ' ',   ' ',   ' ',
    /* -- \0 */    '<',   '<',   '<',   '<',   '<',   '<',   '<',   ' ',   '='
};

void readNumber(char*& p, stack<double>& stk)
{ //将起始于p的子串解析为数值,并存入操作数栈
    stk.push((double)(*p - '0')); //当前数位对应的数值进栈
    double temp;//创建临时的数
    while (isdigit(*(++p))) //只要后续还有紧邻的数字(即多位整数的情况),则
    {
        temp=stk.top(); stk.pop();
        stk.push(temp* 10 + (*p - '0')); //弹出原操作数并追加新数位后,新数值重新入栈
    }
    if ('.' != *p)
    {
        return; //此后非小数点,则意味着当前操作数解析完成
    }
    float fraction = 1; //否则,意味着还有小数部分
    while (isdigit(*(++p))) //逐位加入
    {
        temp=stk.top(); stk.pop();
        stk.push(temp + (*p - '0') * (fraction /= 10)); //小数部分
    }
}

void append(char*& rpn, double opnd)
{ //将操作数接至RPN末尾
    char buf[64];
    if (0.0 < opnd - (int)opnd)
    {
        sprintf(buf, "%f \0", opnd); //浮点格式,或
    }
    else
    {
        sprintf(buf, "%d \0", (int)opnd); //整数格式
    }
    rpn = (char*)realloc(rpn, sizeof(char) * (strlen(rpn) + strlen(buf) + 1)); //扩展空间
    strcat(rpn, buf); //RPN加长
}

void append(char*& rpn, char optr)
{ //将运算符接至RPN末尾
    int n = strlen(rpn); //RPN当前长度(以'\0'结尾,长度n + 1)
    rpn = (char*)realloc(rpn, sizeof(char) * (n + 3)); //扩展空间
    sprintf(rpn + n, "%c ", optr);
    rpn[n + 2] = '\0'; //接入指定的运算符
}


Operator optr2rank(char op)
{ //由运算符转译出编号
    switch (op)
    {
        case '+':
        {
            return ADD; //加
        }
        case '-':
        {
            return SUB; //减
        }
        case '*':
        {
            return MUL; //乘
        }
        case '/':
        {
            return DIV; //除
        }
        case '^':
        {
            return POW; //乘方
        }
        case '!':
        {
            return FAC; //阶乘
        }
        case '(':
        {
            return L_P; //左括号
        }
        case ')':
        {
            return R_P; //右括号
        }
        case '\0':
        {
            return EOE; //起始符与终止符
        }
        default:
        {
            exit(-1); //未知运算符
        }
    }
}

char orderBetween(char op1, char op2) //比较两个运算符之间的优先级
{
    return pri[optr2rank(op1)][optr2rank(op2)];
}

__int64 facI(int n)
{
    __int64 f = 1;
    while (n > 1)
    {
        f *= n--;
    }
    return f;
} //阶乘运算(迭代版)

double calcu(double a, char op, double b)
{ //执行二元运算
    switch (op)
    {
        case '+':
        {
            return a + b;
        }
        case '-':
        {
            return a - b;
        }
        case '*':
        {
            return a * b;
        }
        case '/':
        {
            if (0 == b)
            {
                exit(-1);
            }
            else
            {
                return a / b; //注意:如此判浮点数为零可能不安全
            }
        }
        case '^':
        {
            return pow(a, b);
        }
        default:
        {
            exit(-1);
        }
    }
}

double calcu(char op, double b)
{ //执行一元运算
    switch (op)
    {
        case '!':
        {
            return (double)facI((int)b); //目前仅有阶乘,可照此方式添加
        }
        default:
        {
            exit(-1);
        }
    }
}


double evaluate(char* S, char*& RPN)
{ //对(已剔除白空格的)表达式S求值,并转换为逆波兰式RPN
    stack<double> opnd;
    stack<char> optr; //运算数栈、运算符栈 /*DSA*/任何时刻,其中每对相邻元素之间均大小一致
    char* expr = S;
    optr.push('\0'); //尾哨兵'\0'也作为头哨兵首先入栈
    while (!optr.empty())
    { //在运算符栈非空之前,逐个处理表达式中各字符
        if (isdigit(*S))
        { //若当前字符为操作数,则
            readNumber(S, opnd);
            append(RPN, opnd.top()); //读入操作数,并将其接至RPN末尾
        }
        else //若当前字符为运算符,则
        {
            switch (orderBetween(optr.top(), *S))
            { //视其与栈顶运算符之间优先级高低分别处理
                case '<': //栈顶运算符优先级更低时
                {
                    optr.push(*S); S++; //计算推迟,当前运算符进栈
                    break;
                }
                case '=': //优先级相等(当前运算符为右括号或者尾部哨兵'\0')时
                {
                    optr.pop(); S++; //脱括号并接收下一个字符
                    break;
                }
                case '>':
                { //栈顶运算符优先级更高时,可实施相应的计算,并将结果重新入栈
                    char temp=optr.top(); optr.pop();
                    char op =temp ; append(RPN, op); //栈顶运算符出栈并续接至RPN末尾
                    if ('!' == op)
                    { //若属于一元运算符
                        double pOpnd = opnd.top(); //只需取出一个操作数,并
                        opnd.pop();
                        opnd.push(calcu(op, pOpnd)); //实施一元计算,结果入栈
                    }
                    else
                    { //对于其它(二元)运算符
                        double pOpnd2 = opnd.top();opnd.pop();
                        double pOpnd1 = opnd.top(); opnd.pop();//取出后、前操作数 /*DSA*/提问:
                                                                        //可否省去两个临时变量?
                                                                        //不可以,函数参数计算的顺序是不确定的
                        opnd.push(calcu(pOpnd1, op, pOpnd2)); //实施二元计算,结果入栈
                    }
                    break;
                }
                default:
                {
                    exit(-1); //逢语法错误,不做处理直接退出
                }
            }

        }
    }
    double temp=opnd.top();
    opnd.pop();
    return temp ;//弹出并返回最后的计算结果
}


char* removeSpace ( char* s )
{ //剔除s[]中的白空格
   char* p = s, *q = s;
   while ( true )
   {
       while (isspace(*q))
       {
           q++;
       }
      if ( '\0' == *q )
      {
          *p = '\0';
          return s;
      }
      *p++ = *q++;
   }
}


//教材实例代码包中的测试用例
//值得注意的是这里所使用的测试用例需要在VS2019中点击属性然后手动设置命令参数


/*
int main(int argc, char* argv[])
{ //表达式求值(入口)

    for (int i = 1; i < argc; i++)
    { //逐一处理各命令行参数(表达式)
        system("cls");
        printf("\nPress any key to evaluate: [%s]\a\n", argv[i]);
        getchar();
        char* rpn = (char*)malloc(sizeof(char) * 1);
        rpn[0] = '\0'; //逆波兰表达式
        double value = evaluate(removeSpace(argv[i]), rpn); //求值
        printf("EXPR\t: %s\n", argv[i]); //输出原表达式
        printf("RPN\t: [ %s]\n", rpn); //输出RPN
        printf("Value\t= %f = %d\n", value, (int)value); //输出表达式的值
        free(rpn);
        rpn = NULL;
        getchar();
    }


    system("pause");
    return 0;
}
*/
int main()
{
    char* rpn = (char*)malloc(sizeof(char) * 1);
    rpn[0] = '\0'; //逆波兰表达式
    double value = evaluate(const_cast < char*>("(1+2^3!-4)*(5!-(6-(7-(89-0!))))"), rpn); //求值
    cout << value << endl;
    cout << "the rpn is:" << rpn;
    free(rpn);
    rpn = NULL;
    system("pause");
    return 0;
}

逆波兰表达式求值

double rpnEvaluation(char *expr){//对RPN表达式的求值
    stack<double> Stk;//存放操作数的栈
    while((*expr)!='\0'){//expr尚未扫描完毕,即不等于结束符号,这里的结束符号是'\0'
        if((*expr)==' ') {//这里的分隔符号是空格
        expr++;//跳过分隔符号
        continue;
        }
        if (isdigit(*expr))//
        { //若当前字符为操作数,则
            readNumber(expr,Stk);//将操作数入栈
        }else //若当前字符为运算符,则
        {
            char op =(*expr);//计算当前操作符
            expr++;//运算完成之后计算表达式
             if ('!' == op){//如果是一元运算
                expr++;//运算完成之后计算表达式
                double number = Stk.top();
                Stk.pop();
                Stk.push(calcu(op, number)); //实施一元计算,结果入栈
             }
             else{
                double pOpnd2 = Stk.top();
                Stk.pop();
                double pOpnd1 = Stk.top();
                Stk.pop();
                Stk.push(calcu(pOpnd1, op, pOpnd2)); //实施二元计算,结果入栈
             }

        }
    }
	return Stk.top();
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
逆波兰表达式(Reverse Polish Notation,RPN),也称为后缀表达式,是一种无需使用括号就能明确表达运算顺序的方式。在C++中,如果你需要实现逆波兰表达式求值,可以采用栈的数据结构。基本步骤如下: 1. 初始化两个栈,一个用于存放操作数,另一个用于存放操作符。 2. 遍历输入的逆波兰表达式字符串,对于每个字符: - 如果它是一个数字,就将其压入操作数栈。 - 如果它是运算符,从栈顶弹出足够次数的操作数进行计算,并将结果压回栈顶,然后将当前运算符压入运算符栈。 3. 当遍历完所有字符后,运算符栈顶部剩余的就是最终的结果。 这是一个简单的C++代码示例,使用了STL中的stack: ```cpp #include <iostream> #include <string> #include <stack> int evalPostfix(const std::string& postfix) { std::stack<int> numStack; for (char token : postfix) { if (isdigit(token)) { int num = token - '0'; numStack.push(num); } else { int b = numStack.top(); numStack.pop(); int a = numStack.top(); numStack.pop(); switch (token) { case '+': numStack.push(a + b); break; case '-': numStack.push(a - b); break; case '*': numStack.push(a * b); break; case '/': numStack.push(a / b); break; // 添加其他支持的运算符... } } } return numStack.top(); } int main() { std::string expression = "2 3 + 4 *"; int result = evalPostfix(expression); std::cout << "Result of the RPN expression is: " << result << std::endl; return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值