1. 中缀、前缀和后缀表达式

1.1 中缀表达式

首先,中缀表达式的这个“缀”指运算符在两个操作数的位置。中缀表达式其实就是我们常用的算术表达式,比如 2 + 9 - (32 * (19 - 4) +14),我们很容易就可以得到计算结果,但是对于计算机来说,它们就得对各个运算符进行优先级比较以及弹栈和入栈等操作,其实计算机对于前缀表达式和后缀表达式更容易理解。

1.2 前缀表达式

前缀表达式,也称波兰式,指运算符处于两个操作数的前面,比如 2 + 3,那么前缀表达式就是 + 2 3;对于复杂点的表达式,如 13 * ((3 + 8) * 4),前缀表达式为 * 13 * + 3 8 4,后续分析怎么转换。

1.3 后缀表达式

也称逆波兰式,指运算符处于两操作数后面,比如 2 + 3,后缀表达式为 2 3 +;对于复杂点的表达式,如 13 * ((3 + 8) * 4),后缀表达式为 13 3 8 + 4 * *,后续会讲怎么转换。

2. 中缀表达式到后缀表达式的转换规则

前面我们提到计算机易于理解前缀表达式和后缀表达式,但我们生活中或使用计算器时是中缀表达式,这也就意味着我们需要将中缀表达式转换为前缀或后缀表达式,从而实现计算器的高效计算。

中缀表达式转换为后缀表达式的规则如下:

1.创建运算符栈s1和操作数数组a2,然后扫描中缀表达式;2.如果是操作数,直接放入数组a2;3.如果是运算符,栈s1为空或栈顶符号为左括号,或者优先级比栈顶运算符高,则入栈结束该步骤;否则将s1栈顶运算符弹出放入操作数数组a2,然后重复该步骤3。4.如果是左括号,直接压入运算符栈s1;如果是右括号,依次弹出s1的运算符放入s2,直至遇到左括号结束,并将左、右括号舍弃。5.循环步骤2-4直至表达式扫描结束,将s1的剩余运算符依次弹出放入数组a2,数组a2就是后缀表达式。

以下演示一个较复杂中缀表达式 3 * (11 - 8) - 45 / ((98 - 60) / 10 + 6)  转换后缀表达式的流程,流程如下。

1. 先创建运算符栈s1,和操作数数组a2,然后索引指向中缀表达式的第一位;

2. 3是操作数,放入数组a2的第一个位置,得到a2 = {3};

3. *是运算符,因为栈s1为空,*直接入栈s1,s1 =

4. 遇到左括号,直接压入运算符栈s1,s1 = 

5. 11是操作数,直接放入数组a2,得到a2 = {3, 11};

6. - 是运算符,因为栈顶符号是左括号,- 直接入栈得到s1 = 

7. 8是操作数,直接放入数组a2,得到a2 = {3, 11, 8};

8. 遇到右括号,弹出s1中的运算符 - ,直到遇到左括号结束;此时a2={3, 11, 8, -},s1 = 

9. - 是运算符,因为优先级低于栈顶符号 * ,将s1栈顶符号弹出放入数组a2,a2变为{3, 11, 8, -, *},s1 = 

10. 重复步骤3,由于此时栈为空,则 - 直接入栈,s1 = 

11. 45是操作数,直接放入数组a2,得到a2 = {3, 11, 8, -, *, 45};

12. / 是运算符,而且运算优先级高于s1的栈顶符号 -,所以直接入栈,s1 = 

13. 遇到左括号,直接压入运算符栈s1,s1 = 

14. 遇到左括号,直接压入运算符栈s1,s1 = 

15. 98是操作数,直接放入数组a2,得到a2 = {3, 11, 8, -, *, 45, 98};

16. - 是运算符,因为栈顶符号是左括号,- 直接入栈,得到s1 = 

17. 60是操作数,直接放入数组a2,得到a2 = {3, 11, 8, -, *, 45, 98, 60};

18. 遇到右括号,弹出s1中的运算符 - ,直到遇到左括号结束;此时a2={3, 11, 8, -, *, 45, 98, 60, -},s1 = 

19. / 是运算符,而且栈顶符号是左括号,/ 直接入栈,得到s1 = 

20. 10是操作数,直接放入数组a2,得到a2 = {3, 11, 8, -, *, 45, 98, 60, -, 10};

21. + 是运算符,因为优先级低于栈顶符号 / ,将s1栈顶符号 / 弹出放入数组a2,a2变为{3, 11, 8, -, *, 45, 98, 60, -, 10, /},

      s1 = 

22. + 运算符接着和栈顶符号比较,此时由于栈顶符号是左括号,直接入栈,s1 = 

23. 6是操作数,直接放入数组a2,得到a2 = {3, 11, 8, -, *, 45, 98, 60, -, 10, /, 6};

24. 遇到右括号,弹出s1中的运算符 + ,直到遇到左括号结束;此时a2={3, 11, 8, -, *, 45, 98, 60, -, 10, /, 6, +},

     s1 = 

25. 扫描结束,弹出s1中剩余运算符至数组a2,得到a2 = {3, 11, 8, -, *, 45, 98, 60, -, 10, /, 6, +, /, -}

因此,最终得到的后缀表达式为 3 11 8 - * 45 98 60 - 10 / 6 + / -

代码如下

static int priority(char c1){if((c1 == '*') || (c1 == '/')){return 2;}else{return 1;}
}static List infixToPostfix(String infixExpress){char c;Stack s1 = new Stack();List<String> a2 = new ArrayList();for (int i = 0; i < infixExpress.length(); i++) {c = infixExpress.charAt(i);if((c == '*') || (c == '/') || (c == '+') || (c == '-')){//如果栈空、栈顶元素是左括号,或者运算符优先级高于栈顶元素优先级,则入栈if((s1.isEmpty()) || ((char)s1.lastElement() == '(') || (priority(c) > priority((char)s1.lastElement()))){s1.push(c);}else{a2.add(String.valueOf(s1.pop()));//循环比较,直至运算符可以入栈,结束;否则一直将栈顶元素弹出放入数组a2while(!(s1.isEmpty() || ((char)s1.lastElement() == '(') || (priority(c) > priority((char)s1.lastElement())))){a2.add(String.valueOf(s1.pop()));}s1.push(c);}}else if(c == '('){s1.push(c);}else if(c == ')'){char c2 = (char)s1.pop();//循环比较,直至匹配到左括号结束;否则一直将栈顶元素弹出放入数组a2while(c2 != '('){a2.add(String.valueOf(c2));c2 = (char)s1.pop();}}else if((c >= 48) && (c <= 57)){//如果是最后一位,那么直接将数字放入数组a2,结束if(i == infixExpress.length()-1){a2.add(String.valueOf(c));}else{String s2 = "" + c;int cnt = 1;char c3 = infixExpress.charAt(i+cnt);while((c3 >= 48) && (c3 <= 57)){s2 += c3;cnt++;//一直扫描数字,直至中缀表达式末尾,结束if(i+cnt < infixExpress.length()){c3 = infixExpress.charAt(i+cnt);}else{break;}}i += (cnt-1);a2.add(s2);}}else{//如果字符是空格或者表达式最后一个字符是 = ,不操作,否则表达式异常if(((i == infixExpress.length()-1) && (c == '=')) || (c == ' ')){}else{System.out.println("Express is fault");}}}//最后需要将s1剩余的运算符弹出,放入数组a2中。while(! s1.isEmpty()){a2.add(String.valueOf(s1.pop()));}return a2;}

3. 后缀表达式的计算原则

我们得到后缀表达式 3 11 8 - * 45 98 60 - 10 / 6 + / -,那么怎么计算呢?其实,后缀表达式计算原则很简单,主要分为三步,如下

1. 创建一个栈,并且从左至右扫描表达式;

2. 遇到数字,将数字压入栈中;遇到运算符则弹出栈顶的两个元素,使用运算符进行计算,然后将计算结果再压入栈中;

3. 重复步骤2直到表达式扫描结束,最后弹出栈顶元素就是计算结果。

以上示例后缀表达式的计算步骤如下:

1. 创建栈stack,扫描表达式,将数字3, 11, 8依次压入栈stack,得到stack = 

2. 遇到运算符 - ,栈stack弹出 8 和 11,计算 11 - 8 得到3,入栈得到stack = 

3. 遇到运算符 * ,栈stack弹出 3 和 3,计算得到 9,入栈得到stack = 

4. 将数字45, 98, 60压入栈,得到stack = 

5. 遇到运算符 - ,栈stack弹出 60 和 98,计算 98- 60得到38,入栈得到stack = 

6. 将数字10压入栈,得到stack = 

7. 遇到运算符 / ,栈stack弹出 10 和 38,计算 38 / 10得到3 (java整数相除得到整数3,其它变成语言可得到3.8),入栈得到

    stack = 

8. 将数字6压入栈,得到stack = 

9. 遇到运算符 + ,栈stack弹出 6 和 3,计算 3 + 6 得到9,入栈得到stack = 

10. 遇到运算符 / ,栈stack弹出 9 和 45,计算 45 / 9 得到5,入栈得到stack = 

11. 遇到运算符 - ,栈stack弹出 5 和 9,计算 9 - 5 得到4,入栈得到stack = 

12. 计算结束,弹出栈顶元素 4,所以计算结果为4.

代码如下

static String calculate(List<String> ls){Stack<String> st_result = new Stack();for (String s:ls) {// 因为这个计算后缀表达式的方法是和中缀表达式转后缀表达式联合使用的,而且中缀表达式// 转换后缀表达式的方法中考虑了运算符异常等异常,所以以下代码不考虑异常情况if(("-".equals(s)) || ("+".equals(s)) || ("*".equals(s)) || ("/".equals(s))){int num_back = Integer.valueOf(st_result.pop());int num_front = Integer.valueOf(st_result.pop());switch(s){case "-":st_result.push((num_front - num_back) + "");break;case "/":st_result.push((num_front / num_back) + "");break;case "+":st_result.push((num_front + num_back) + "");break;case "*":st_result.push((num_front * num_back) + "");break;}}else {st_result.push(s);}}return st_result.pop();}

完整测试代码以运行结果如下

static int priority(char c1){if((c1 == '*') || (c1 == '/')){return 2;}else{return 1;}}static List infixToPostfix(String infixExpress){char c;Stack s1 = new Stack();List<String> a2 = new ArrayList();for (int i = 0; i < infixExpress.length(); i++) {c = infixExpress.charAt(i);if((c == '*') || (c == '/') || (c == '+') || (c == '-')){//如果栈空、栈顶元素是左括号,或者运算符优先级高于栈顶元素优先级,则入栈if((s1.isEmpty()) || ((char)s1.lastElement() == '(') || (priority(c) > priority((char)s1.lastElement()))){s1.push(c);}else{a2.add(String.valueOf(s1.pop()));//循环比较,直至运算符可以入栈,结束;否则一直将栈顶元素弹出放入数组a2while(!(s1.isEmpty() || ((char)s1.lastElement() == '(') || (priority(c) > priority((char)s1.lastElement())))){a2.add(String.valueOf(s1.pop()));}s1.push(c);}}else if(c == '('){s1.push(c);}else if(c == ')'){char c2 = (char)s1.pop();//循环比较,直至匹配到左括号结束;否则一直将栈顶元素弹出放入数组a2while(c2 != '('){a2.add(String.valueOf(c2));c2 = (char)s1.pop();}}else if((c >= 48) && (c <= 57)){//如果是最后一位,那么直接将数字放入数组a2,结束if(i == infixExpress.length()-1){a2.add(String.valueOf(c));}else{String s2 = "" + c;int cnt = 1;char c3 = infixExpress.charAt(i+cnt);while((c3 >= 48) && (c3 <= 57)){s2 += c3;cnt++;//一直扫描数字,直至中缀表达式末尾,结束if(i+cnt < infixExpress.length()){c3 = infixExpress.charAt(i+cnt);}else{break;}}i += (cnt-1);a2.add(s2);}}else{//如果字符是空格或者表达式最后一个字符是 = ,不操作,否则表达式异常if(((i == infixExpress.length()-1) && (c == '=')) || (c == ' ')){}else{System.out.println("Express is fault");}}}//最后需要将s1剩余的运算符弹出,放入数组a2中。while(! s1.isEmpty()){a2.add(String.valueOf(s1.pop()));}return a2;}static String calculate(List<String> ls){Stack<String> st_result = new Stack();for (String s:ls) {// 因为这个计算后缀表达式的方法是和中缀表达式转后缀表达式联合使用的,而且中缀表达式// 转换后缀表达式的方法中考虑了运算符异常等异常,所以以下代码不考虑异常情况if(("-".equals(s)) || ("+".equals(s)) || ("*".equals(s)) || ("/".equals(s))){int num_back = Integer.valueOf(st_result.pop());int num_front = Integer.valueOf(st_result.pop());switch(s){case "-":st_result.push((num_front - num_back) + "");break;case "/":st_result.push((num_front / num_back) + "");break;case "+":st_result.push((num_front + num_back) + "");break;case "*":st_result.push((num_front * num_back) + "");break;}}else {st_result.push(s);}}return st_result.pop();}public static void main(String[] args) {String s = "3 * (11 - 8) - 45 / ((98 - 60) / 10 + 6)";List posefixList = infixToPostfix(s);System.out.print("后缀表达式为:");for (Object o:posefixList) {System.out.print(o + " ");}System.out.println("\n后缀表达式计算结果是 " + calculate(posefixList));}

前缀、中缀和后缀表达式详解,中缀表达式到后缀表达式的转换规则,以及后缀表达式的计算规则,附计算代码相关推荐

  1. [深入学习C#]LINQ查询表达式详解(1)——基本语法、使用扩展方法和Lambda表达式简化LINQ查询

    此文章非原创,转载自诗人江湖老,原文地址 在Git上下载源码 在工程中我们少不了要定义类或者结构去储存数据,这些数据将被临时地储存在内存中,现在我们想要对其完成一些类似于查找.过滤等等常见的任务的时候 ...

  2. 前缀、中缀、后缀表达式详解

    前缀.中缀.后缀表达式详解 博客说明 文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢! 介绍 前缀.中缀.后缀表达式是对表达式的不同记法,其 ...

  3. pyzabbix 删除触发器_zabbix设置邮件报警, Zabbix常用Key值, zabbix触发器表达式详解, zabbix制作自己模板...

    zabbix设置邮件报警 方法一:说明此方法,会产生中文乱码问题 第一步:首先安装mailx组件并配置好能够通过三方邮箱发送邮件 yum -y install mailx 然后编辑mailx的配置文件 ...

  4. java拉姆达表达式事例,Java Lambda表达式详解和实例

    简介 Lambda表达式是Java SE 8中一个重要的新特性.lambda表达式允许你通过表达式来代替功能接口. lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体( ...

  5. java lambda表达式详解_Java8新特性Lambda表达式详解

    课程目标: 通过本课程的学习,详细掌握Java8新特性之Lambda表达式: 适用人群:有Java基础的开发人员: 课程概述:从Java 8出现以来lambda是最重要的特性之一,它可以让我们用简洁流 ...

  6. cron表达式详解 Elastic-Job名次解释

    #1.cron:cron表达式,用于配置作业触发时间 #2.shardingTotalCount:作业分片总数 #3.shardingItemParameters:分片序列号和参数用等号分隔,多个键值 ...

  7. 产品型号 计算机硬件,主流CPU产品型号后缀名详解_CPUCPU评测-中关村在线

    ◆ 主流CPU产品型号后缀名详解 CPU作为一台电脑中最为重要的核心部件,其硬件性能的高低直接决定着个人电脑整机的性能定位.从目前两大处理器厂商Intel和AMD所推出的产品看,其产品型号后缀的微小差 ...

  8. SpringBoot使用AOP,PointCut表达式详解以及使用

    SpringBoot使用AOP,PointCut表达式详解以及使用 1.相关注解 2.PointCut 表达式详解 2.1 execution: 2.1 within: 2.3. this: 2.4. ...

  9. Spring定时器corn表达式详解

    1.结构 Spring定时器corn表达式详解 2.各字段的含义 字段 允许值 允许的特殊字符 秒 0~59 - * / 分 0~59 - * / 小时 0~23 - * / 日期 1~31 - * ...

  10. 英语词源基础之后缀-ion详解: -ion,-tion,-ction,-ption,-ation,-ition的区别

    英语词源基础之后缀-ion详解 来自古法语名词后缀-ion,表情况或状态,来自拉丁语名词后缀-io的宾格形式-ionem,表情况或状态,来自词干元音i+onem. 在拼写上,该后缀在英语中包括拼写形式 ...

最新文章

  1. 基于libmad库的MP3解码简析
  2. Eclipse启动时报错
  3. CentOS系统代理配置
  4. hmaster和datanaode启动后很快停止_Oracle RAC自启动
  5. NBA史上薪水最高球员排行榜
  6. wxPython事件处理
  7. ASP.NET MVC3 控制器
  8. gcc g++ Linux下动态库_静态库
  9. 零基础Python知识点回顾(一)
  10. Java核心(四)你不知道的数据集合
  11. JavaScript中__proto__与prototype的关系
  12. Linux中qt编写登录
  13. 为什么要是用R语言?
  14. kafka sqs 与_RabbitMQ和Kafka的比较
  15. 关于SETUP FACTORY %AppFolder%写入注册表问题
  16. 完全数(Perfect Number)
  17. 【软件工程】对软件工程课程的希望及个人目标
  18. 连接中国移动彩信网关发送彩信
  19. linux内存双通道,两根内存就是双通道?太年轻
  20. PostgreSQL 之 学籍管理示例

热门文章

  1. Field XXXX input value is longer than screen field-BDC
  2. SAP BPC系统架构
  3. SAP获利能力报表常用Tcode的区别
  4. SD Price公式(例程,即Formula)
  5. MM中如何更改物料的评估类
  6. MIGO+103收货到GR冻结库存和MB1B+344从非限制到冻结区别?
  7. 16款新品发布,数据揭秘小米MIX荣归背后逻辑
  8. 联盟链赛道上,微众、蚂蚁、百度、京东等“正规军”能崛起吗?
  9. 新消费风口再“热闹”,红利依然握在伊利、康师傅们手里
  10. java中xpath_java-xpath学习