首页 > 其他分享 >22张图带你深入剖析前缀、中缀、后缀表达式以及表达式求值

22张图带你深入剖析前缀、中缀、后缀表达式以及表达式求值

时间:2022-09-19 10:58:23浏览次数:65  
标签:遍历 中缀 后缀 运算符 求值 表达式

22张图带你深入剖析前缀、中缀、后缀表达式以及表达式求值

一、基本概念

在本篇文章当中主要跟大家介绍以下几点

  • 前缀、中缀和后缀表达式。
  • 如何将中缀表达式转化成后缀表达式。
  • 如何使用后缀表达式进行求值。

表达式求值这是一个比较经典的计算机系统基础问题,但是整个过程比较抽象,本文主要通过图解的方法帮助大家理解这个问题。

表达式介绍

后缀表达式也称作逆波兰表达式,前缀表达式也称作波兰表达式,这个是因为这是由波兰数学家杨-武卡谢维奇提出来的,用于简化命题逻辑的一种方法。

中缀表达式

我们常见的数学表达式就是中缀表达式,比如说:\(1+2\),像这种我们从小到大经常见到的表达式就叫做中缀表达式,这个表达式的特点就是将运算符(加减乘除)放在两个操作数(数字)中间。

后缀表达式

后缀表达式和中缀表达式的最大区别就是,它不是将运算符放在操作数中间,而是将运算符放在操作数后面,比如上面的中缀表达式\(1+2\)转化成后缀表达式就为\(12+\)。

前缀表达式

前缀表达式就是将运算符放在操作数的前面,比如上面的中缀表达式\(1+2\)转化成前缀表达式之后为\(+12\)。

二、人脑模拟中缀表达式转后缀表达式

上面的表达式还是比较简单,可能不足以帮助大家好好理解表达式之间的转化过程。

中缀表达式:\(\large A+B∗(C−D)−E/F\)

现在我们来将上面的中缀表达式转化成后缀表达式,我们的第一个计算的部分如下(括号里面的优先计算):

根据我们的转化原理:将运算符放在操作数后面,

上面的得到的后缀表达式继续进行转化(现在需要计算\(B\)乘以后面的那个部分):

继续进行转化(从左往后进行计算):

继续进行转化(除法的优先级比减法高):

得到最终的结果:

三、程序计算中缀表达式转化成后缀表达式

将中缀表达式转化成后缀表达式有以下几条规则(下面的栈是存储操作符的栈,而且只存储操作符):

  • 从左到右进行遍历。
  • 遇到操作数,直接加入到后缀表达式当中。
  • 遇到界限符。遇到“(”直接入栈,遇到“)”则依次弹出栈内运算符并加入后缀表达式,直到弹出“(” 为止,注意:“(” 不加入后缀表达式。
  • 遇到运算符。依次弹出栈中优先级高于或等于当前运算符的所有运算符,并加入后缀表达式,若碰到“(”或栈空则停止。之后再把当前运算符入栈。

现在我们根据上面的规则来将上文当中的中缀表达式转化成后缀表达式。

  • 遍历到\(A\),根据第二条规则,将A加入到后缀表达式当中,当前的后缀表达式为:\(A\)。
  • 现在遍历到加号,根据前面的规则需要弹出栈里面优先级大的运算符,再将加号加入到栈中,当前的后缀表达式为\(A\)。
  • 遍历到\(B\),直接加入到后缀表达式当中,目前的后缀表达式为\(AB\)。
  • 遍历到∗,根据规则直接将其加入到栈中,当前后缀表达式为\(AB\)。

遍历到\((\),根据规则直接将其加入到栈中,当前后缀表达式为\(AB\)。

  • 遍历到\(C\),则直接将其加入到后缀表达式当中,当前后缀表达式为\(ABC\)。
  • 遍历到\(−\),根据规则将其加入到栈中,当前后缀表达式为\(ABC\)。
  • 遍历到\(D\),则将其直接加入到后缀表达式当中,当前的后缀表达式为\(ABCD\)。

  • 遍历到\()\),则需要将栈中的符号弹出,直到遇到\((\),当前的后缀表达式为\(ABCD−\)。

  • 遍历到\(−\),需要将栈中优先级大于等于\(−\)的运算符弹出,则当前的后缀表达式为\(ABCD−∗+\),再将\(−\)压入栈中。
  • 遍历到\(E\),直接将数字加入到后缀表达式当中,则当前的后缀表达式为\(ABCD−∗+E\)。
  • 遍历到\(/\),将栈中优先级大于等于\(/\)的运算符,再将\(/\)压入到栈中,当前的后缀表达式为\(ABCD−∗+E\)。
  • 遍历到F,直接将其加入到后缀表达式当中,则当前的后缀表达式为\(ABCD−∗+EF\)。

  • 最终将栈中所有的运算符都弹出,得到的后缀表达式为\(ABCD−∗+EF/−\)

经过上面的过程就可以将一个中缀表达式转成后缀表达式了,大家如果想要代码实现,只需要在遍历数据的时候根据上面四个规则一个个进行判断即可,程序并不困难,就是逻辑稍微有点多,需要考虑更多的问题,在写代码的时候需要细致一点。

三、程序计算后缀表达式求值

在前文当中我们已经得到了表达式\(A+B∗(C−D)−E/F\)的后缀表达式为\(ABCD−∗+EF/−\)。现在我们需要将这个后缀表达式进行求值。根据后缀表达式求值主要有以下两条规则:

  • 如果遇到数字直接将其加入到数字栈。
  • 如果遇到操作符直接从操作数栈弹出两个数据进行对应的运算,再将运算结果加入到栈中。

现在我们进行后缀表达式的求值过程;

  • 首先前面四个\(ABCD\)都是数字,根据上面提到的第一条规则,我们都需要将数字压入到栈当中,因此遍历四个数字之后,情况如下:

现在遍历到\(−\),我们需要将\(D\)和\(C\)弹出,然后进行\(−\)操作的运算,再将结果压入栈中。

  • 现在遍历到\(∗\),我们需要将\(C−D=M\)和\(B\)弹出,进行乘法运算,然后将结果压入栈中。
  • 现在我们遍历到\(+\),需要将栈中剩余的两个数弹出,进行加法运算,在将结果压栈。
  • 遍历\(EF\)都需要将这两个数压入到栈当中。
  • 现在遍历到\(/\),需要进行除法运算,在将得到的结果压入到栈中。
  • 最后遍历到\(−\),将栈中的两个数弹出栈,进行减法运算得到最后的结果。

相信经过上面过程你对后缀表达式求值的过程已经非常清楚了。

五、计算表达式的值

#include <bits/stdc++.h>

using namespace std;

/*
测试用例I:
(2+2)*(1+1)

答案:8

测试用例II:注意:不能在输入中带空格,会算错!!!
2+(3*4)-((5*9-5)/8-4)

答案:13
*/

stack<int> num; //数字栈
stack<char> op; //操作符栈

//优先级表
unordered_map<char, int> h{{'+', 1},
                           {'-', 1},
                           {'*', 2},
                           {'/', 2}};

/**
 * 功能:计算两个数的和差积商
 */
void eval() {
    int a = num.top(); //第二个操作数
    num.pop();

    int b = num.top(); //第一个操作数
    num.pop();

    char p = op.top(); //运算符
    op.pop();

    int r = 0; //结果
    //计算结果
    if (p == '+')
        r = b + a;
    else if (p == '-')
        r = b - a;
    else if (p == '*')
        r = b * a;
    else if (p == '/')
        r = b / a;
    //结果入栈
    num.push(r);
}

//读取连续的数字
void readNum(string s, int &pos, int &x) {
    while (pos < s.size() && isdigit(s[pos])) {
        x = x * 10 + s[pos] - '0';
        pos++;
    }
    //在循环里加冒了,需要返回一步,方便下一个人开始
    pos--;
}

int main() {
    //读入表达式
    string s;
    cin >> s;
    //遍历字符串的每一位
    for (int i = 0; i < s.size(); i++) {
        // 1、数字则入栈
        if (isdigit(s[i])) {
            //读出完整的数字
            int x = 0;
            readNum(s, i, x);
            num.push(x); //数字入栈
        }
        // 2、左括号无优先级,直接入栈
        else if (s[i] == '(')
            op.push(s[i]);
        // 3、右括号计算最近一对括号里面的
        else if (s[i] == ')') {
            //一直找到到左括号
            while (op.top() != '(') eval(); //将左右括号之间的计算完,维护回栈里
            //左括号出栈
            op.pop();
        } else { // 4、运算符
            //如果待入栈运算符优先级低,则先计算
            while (op.size() && h[op.top()] >= h[s[i]]) eval();
            op.push(s[i]); //操作符入栈
        }
    }
    // 5、//剩余的进行计算
    while (op.size()) eval();
    // 6、输出结果
    cout << num.top() << endl;
    return 0;
}

标签:遍历,中缀,后缀,运算符,求值,表达式
From: https://www.cnblogs.com/littlehb/p/16706908.html

相关文章

  • C++11 -- 匿名函数(lambda 表达式)
    0.一道题目引入关于sb力扣定义外部函数和变量报错这件事最初我定义了一个\(cmp\)函数用来对\(vector\)排序,和一个全局变量\(unordered\_map\)用来记录元素个数......
  • Linux三剑客之一——grep及正则表达式的学习
    1.grep理论1.1grep作用1.2grep参数及说明1.3 基本正则表达式BRE集合1.4 扩展正则表达式ERE集合2.grep实践2.1grep基本参数2.2 grep正则表达式3.总结 1.gr......
  • JAVA Lambda表达式
    JAVALambda表达式函数式在数学中,函数就是有输入量,输出量的一套计算方案,也就是拿数据做操作面向对象思想强调“必须通过对象形式来做事情”函数式思想则尽量忽略......
  • 正则表达式的先行断言(lookahead)和后行断言(lookbehind)
    正则表达式的先行断言和后行断言一共有4种形式:(?=pattern)零宽正向先行断言(zero-widthpositivelookaheadassertion)(?!pattern)零宽负向先行断言(zero-widthnegativ......
  • 计算表达式
    后缀表达式运算方法就是从左往右扫描,遇到一个数字就将其压到栈中如果遇到了一个运算符,就弹出栈最上面的两个数进行运算,再将新数放回到栈中前缀表达式搜索结果和后缀......
  • python re包 正则表达式
    Python正则表达式正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。在python中提供了一个使用正则的模块re。re模块使Python语言......
  • 正则表达式
    定位符^:以某某开头(^a:以a开头)$:以某某结尾(b$:以b结尾)匹配符.:匹配任意一个字符(^a.b$:以a字符开头b字符结尾的三位字符串,中间位可以是任意字符)[]:匹配一个......
  • Lambda表达式和Stream API
    Lambda表达式和StreamAPILambda表达式是JavaSE8中一个重要的新特性。lambda表达式允许你通过表达式来代替功能接口。lambda表达式就和方法一样,它提供了一个正常的参......
  • cronTrigger 与 cron表达式星期有差异
    昨天写完定时任务 今天一测  不行阿 这个周有问题 月和日都正常跑到数据库一看cron表达式没问题阿那就是后台解析有问题了 后台解析用的是cronTriggerC......
  • Python3 正则表达式
    正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。Python自1.5版本起增加了re模块,它提供Perl风格的正则表达式模式。re模块使Py......