后缀表达式求值

后缀表达式又叫逆波兰表达式,其求值过程可以用到栈来辅助存储。例如要求值的后缀表达式为:1 2 3 + 4 * + 5 -,则求值过程如下:

  1. 遍历表达式,遇到数字时直接入栈,栈结构如下

    

  2. 接着读到 “+”操作符,则将栈顶和次栈顶元素出栈与操作符进行运算,执行 2 + 3操作,并将结果5压入栈中,此时栈结构如下

    

3.  继续读到4,是数字则直接压栈,此时栈结构如下

    

  4. 继续向后读取,此时读取到操作符“*”,则将栈顶和次栈顶元素出栈与操作符进行运算,即执行 5 * 4 ,然后将结果20压入栈中,此时栈结构如下

    

  5. 继续向后读取,此时读到操作符“+”,则将栈顶和次栈顶元素出栈与操作符进行运算,即执行1 + 20,然后将结果21压入栈中,此时栈结构如下

    

  6. 继续向后读取,此时读到数字5,则直接将数字压栈,栈结构如下

    

  7. 读取到最后一个为操作符,将栈顶和次栈顶元素出栈与操作符进行运算,即执行 21- 5(注意顺序 次栈顶-栈顶),然后将结果16压入栈中,此时栈结构如下

    

  此时栈顶元素即为表达式的结果。(注:为方便理解,这里栈顶指针向下移动后,上面元素直接去掉了,实际情况数据还会存在对应位置,只是通过栈顶指针读取不到,等待GC)

中缀表达式转后缀表达式

中缀表达式为我们人类能识别的方式,而后缀表达式是计算机进行运算的方式(即我们上述的过程)。

转换规则

  1)我们使用一个stack栈结构存储操作符,用一个List结构存储后缀表达式结果

  2)首先读取到数字,直接存入list中

  3)当读取到左括号"("时,直接压栈,当读取到运算符时,分两种情况讨论

    a.当运算符栈为空或者栈顶操作符的优先级小于当前运算符优先级时(如+和-的优先级低于 * 和 /),直接入栈

    b.当运算符不为空时且栈顶操作符的优先级大于或等于当前运算符优先级时,循环执行出栈操作并加入list中,直到遇到优先级小于当前运算符的元素为止。循环执行完后再将当前运算符压栈。另外需要注意的是,只有遇到右括号时,左括号才出栈

  4) 当遇到右括号")"时,循环执行出栈操作并加入到list中,直到遇到左括号为止。并将左括号弹出,但不加入list中

  5) 表达式的值读取完后,将操作符栈中的所有元素弹出并加入到list中

  执行完上面步骤后,list中存储的顺序即为我们转换后的后缀表达式的结果

转换实例

  下面利用上面定义的转换规则,将表达式 1+((2+3)*4)-5 以图解的方式描述其转换过程

  1.首先定义一个存储操作符的栈 Stack<String> stack = new Stack<>() ,和一个存储最终后缀表达式的列表 List<String> list = new ArrayList<>()

  2.读取表达式,首先读取到数字 1 ,按照上述规则,直接添加至list中。此时stack和list结构如下

  

  3.然后读取到操作符 + ,此时stack为空,按照上述规则,直接入栈,此时stack和list结构如下

  

  4.接下来的两次读取都是左括号,按照我们的规则,左括号直接入栈,此时stack和list结构如下

  

  5.接着读取到数字2,按照我们的规则,数字直接加入list中,此时stack和list结构如下

  

  6.接着读取到操作符+,按照我们的规则,此时栈不为空且栈顶元素为左括号,而只有遇到右括号时,左括号才出栈,所以+运算符直接入栈,此时stack和list结构如下

  

  7. 接着读取到数字3,根据我们的规则,数字直接加入list中,此时stack和list结构如下

  

  8. 继续向后读取,读到到右括号 ")",按照我们的规则,执行stack出栈并加入list中操作,直到遇到左括号,并将左括号弹出,但不加入list中,此时stack和list结构如下

  

  9.接着读取到操作符 * ,按照我们的规则,此时栈顶元素为左括号,只需将操作符压栈即可,此时stack和list结构如下

  

  10.接下来读取到数字4,按照规则直接将数字加入list中即可,此时stack和list结构如下

  

  11.接下来读取到右括号")",按照我们的规则,执行stack出栈并加入list中操作,直到遇到左括号,并将左括号弹出,但不加入list中,此时stack和list结构如下

  

  12.继续向后读取,此时读取到操作符-,按照我们的规则,当栈不为空且当前优先级小于等于栈顶操作符优先级时,循环执行出栈并加入list操作。循环执行完再将当前操作符入栈

  

  13.读取最后一个元素为数字5,按照规则,直接加入list中即可。当表达式读取完后,依此弹出操作符栈中的所有元素,并加入list中,此时stack和list结构如下

  

  此时list中的顺序即为我们转换后的后缀表达式的顺序,即:1 2 3 + 4 * + 5 -

转换代码

  1. private static List<String> parseToSuffixExpression(List<String> expressionList) {
  2. //创建一个栈用于保存操作符
  3. Stack<String> opStack = new Stack<>();
  4. //创建一个list用于保存后缀表达式
  5. List<String> suffixList = new ArrayList<>();
  6. for(String item : expressionList){
  7. //得到数或操作符
  8. if(isOperator(item)){
  9. //是操作符 判断操作符栈是否为空
  10. if(opStack.isEmpty() || "(".equals(opStack.peek()) || priority(item) > priority(opStack.peek())){
  11. //为空或者栈顶元素为左括号或者当前操作符大于栈顶操作符直接压栈
  12. opStack.push(item);
  13. }else {
  14. //否则将栈中元素出栈如队,直到遇到大于当前操作符或者遇到左括号时
  15. while (!opStack.isEmpty() && !"(".equals(opStack.peek())){
  16. if(priority(item) <= priority(opStack.peek())){
  17. suffixList.add(opStack.pop());
  18. }
  19. }
  20. //当前操作符压栈
  21. opStack.push(item);
  22. }
  23. }else if(isNumber(item)){
  24. //是数字则直接入队
  25. suffixList.add(item);
  26. }else if("(".equals(item)){
  27. //是左括号,压栈
  28. opStack.push(item);
  29. }else if(")".equals(item)){
  30. //是右括号 ,将栈中元素弹出入队,直到遇到左括号,左括号出栈,但不入队
  31. while (!opStack.isEmpty()){
  32. if("(".equals(opStack.peek())){
  33. opStack.pop();
  34. break;
  35. }else {
  36. suffixList.add(opStack.pop());
  37. }
  38. }
  39. }else {
  40. throw new RuntimeException("有非法字符!");
  41. }
  42. }
  43. //循环完毕,如果操作符栈中元素不为空,将栈中元素出栈入队
  44. while (!opStack.isEmpty()){
  45. suffixList.add(opStack.pop());
  46. }
  47. return suffixList;
  48. }
  49. /**
  50. * 判断字符串是否为操作符
  51. * @param op
  52. * @return
  53. */
  54. public static boolean isOperator(String op){
  55. return op.equals("+") || op.equals("-") || op.equals("*") || op.equals("/");
  56. }
  57.  
  58. /**
  59. * 判断是否为数字
  60. * @param num
  61. * @return
  62. */
  63. public static boolean isNumber(String num){
  64. return num.matches("\\d+");
  65. }
  66.  
  67. /**
  68. * 获取操作符的优先级
  69. * @param op
  70. * @return
  71. */
  72. public static int priority(String op){
  73. if(op.equals("*") || op.equals("/")){
  74. return 1;
  75. }else if(op.equals("+") || op.equals("-")){
  76. return 0;
  77. }
  78. return -1;
  79. }

  这里为了方便操作,将原中缀表达式字符串转换为list结构,转换list的代码如下

  1. /**
  2. * 将表达式转为list
  3. * @param expression
  4. * @return
  5. */
  6. private static List<String> expressionToList(String expression) {
  7. int index = 0;
  8. List<String> list = new ArrayList<>();
  9. do{
  10. char ch = expression.charAt(index);
  11. if(ch < 47 || ch > 58){
  12. //是操作符,直接添加至list中
  13. index ++ ;
  14. list.add(ch+"");
  15. }else if(ch >= 47 && ch <= 58){
  16. //是数字,判断多位数的情况
  17. String str = "";
  18. while (index < expression.length() && expression.charAt(index) >=47 && expression.charAt(index) <= 58){
  19. str += expression.charAt(index);
  20. index ++;
  21. }
  22. list.add(str);
  23. }
  24. }while (index < expression.length());
  25. return list;
  26. }

  注:char类型本质为int类型,查看assic码表可知,0~9对应的char在 47~58之间,所以代码依此来判断是数字还是操作符。另外代码中有判断多位数情况,请注意

  下面展示测试代码

  1. public static void main(String []args){
  2.  
  3. String expression = "1+((2+3)*4)-5";
  4. List<String> expressionList = expressionToList(expression);
  5. System.out.println("expressionList="+expressionList);
  6. //将中缀表达式转换为后缀表达式
  7. List<String> suffixList = parseToSuffixExpression(expressionList);
  8. System.out.println(suffixList);
  9. }

  测试结果如下:

  与我们上述描述的结果相同,以上即为中缀表达式转换后缀表达式的过程及相关代码。另外附上根据后缀表达式求值的代码,感兴趣的可以参考

  1. /**
  2. * 根据后缀表达式list计算结果
  3. * @param list
  4. * @return
  5. */
  6. private static int calculate(List<String> list) {
  7. Stack<Integer> stack = new Stack<>();
  8. for(int i=0; i<list.size(); i++){
  9. String item = list.get(i);
  10. if(item.matches("\\d+")){
  11. //是数字
  12. stack.push(Integer.parseInt(item));
  13. }else {
  14. //是操作符,取出栈顶两个元素
  15. int num2 = stack.pop();
  16. int num1 = stack.pop();
  17. int res = 0;
  18. if(item.equals("+")){
  19. res = num1 + num2;
  20. }else if(item.equals("-")){
  21. res = num1 - num2;
  22. }else if(item.equals("*")){
  23. res = num1 * num2;
  24. }else if(item.equals("/")){
  25. res = num1 / num2;
  26. }else {
  27. throw new RuntimeException("运算符错误!");
  28. }
  29. stack.push(res);
  30. }
  31. }
  32. return stack.pop();
  33. }

  测试运算代码如下

  1. public static void main(String []args){
  2.  
  3. String expression = "1+((2+3)*4)-5";
  4. List<String> expressionList = expressionToList(expression);
  5. System.out.println("中缀表达式转为list结构="+expressionList);
  6. //将中缀表达式转换为后缀表达式
  7. List<String> suffixList = parseToSuffixExpression(expressionList);
  8. System.out.println("对应的后缀表达式列表结构="+suffixList);
  9. //根据后缀表达式计算结果
  10. int calculateResult = calculate(suffixList);
  11. System.out.printf(expression+"=%d\n",calculateResult);
  12. }

  计算结果如下

总结

  中缀表达式转后缀表达式的难点在于转换规则,当然这个规则是研究算法的人已经帮我们制定好的,我们只需要按照这个规则实现代码即可。如果上述代码有问题可在留言区回复,谢谢

中缀表达式转后缀表达式(Java代码实现)的更多相关文章

  1. 【java】中缀表达式转后缀表达式 java实现

    算法: 中缀表达式转后缀表达式的方法:1.遇到操作数:直接输出(添加到后缀表达式中)2.栈为空时,遇到运算符,直接入栈3.遇到左括号:将其入栈4.遇到右括号:执行出栈操作,并将出栈的元素输出,直到弹出 ...

  2. Java堆栈的应用2----------中缀表达式转为后缀表达式的计算Java实现

    1.堆栈-Stack 堆栈(也简称作栈)是一种特殊的线性表,堆栈的数据元素以及数据元素间的逻辑关系和线性表完全相同,其差别是线性表允许在任意位置进行插入和删除操作,而堆栈只允许在固定一端进行插入和删除 ...

  3. 数据结构Java实现06----中缀表达式转换为后缀表达式

    本文主要内容: 表达式的三种形式 中缀表达式与后缀表达式转换算法 一.表达式的三种形式: 中缀表达式:运算符放在两个运算对象中间,如:(2+1)*3.我们从小做数学题时,一直使用的就是中缀表达式. 后 ...

  4. .net表达式计算器(中缀表达式转后缀表达式,支持20多个数学函数,支持函数嵌套)

    最近在网上查了一下表达工计算器的类库,发现Java版本的有一个比较成熟的叫W3EVal,好像是一个IBM工程师写的,.net就很少了(可能是我了解不够多),但投机取巧的实现思路有很多,比如: (1)将 ...

  5. 3-06. 表达式转换(25)(中缀表达式转后缀表达式ZJU_PAT)

    题目链接:http://pat.zju.edu.cn/contests/ds/3-06 算术表达式有前缀表示法.中缀表示法和后缀表示法等形式. 日常使用的算术表达式是採用中缀表示法,即二元运算符位于两 ...

  6. 中缀表达式转后缀表达式(Python实现)

    中缀表达式转后缀表达式 中缀表达式转后缀表达式的规则: 1.遇到操作数,直接输出: 2.栈为空时,遇到运算符,入栈: 3.遇到左括号,将其入栈: 4.遇到右括号,执行出栈操作,并将出栈的元素输出,直到 ...

  7. Python与数据结构[1] -> 栈/Stack[1] -> 中缀表达式与后缀表达式的转换和计算

    中缀表达式与后缀表达式的转换和计算 目录 中缀表达式转换为后缀表达式 后缀表达式的计算 1 中缀表达式转换为后缀表达式 中缀表达式转换为后缀表达式的实现方式为: 依次获取中缀表达式的元素, 若元素为操 ...

  8. 栈的简单应用之中缀表达式转后缀表达式(C语言实现逆波兰式)

    一.前言   普通人在书写计算式时会选择中缀表达式,这样符合人脑的认知习惯.可计算机处理时后缀表达式才能使处理速度更快,其原因是利用堆栈结构减少计算机内存访问.同时它也是一个很好锻炼栈这个数据结构的应 ...

  9. 【Weiss】【第03章】练习3.20:中缀表达式转后缀表达式

    [练习3.20] a.编写一个程序将中缀表达式转换为后缀表达式,该中缀表达式含括号及四则运算. b.把幂操作符添加到你的指令系统中去. c.编写一个程序将后缀表达式转化为中缀表达式. Answer: ...

随机推荐

  1. CentOS7下使用SonatypeNexus3搭建Docker私有仓库

    前置条件:安装docker(如果机器上没有安装的话) //安装一些必要的系统工具: sudo yum install -y yum-utils device-mapper-persistent-dat ...

  2. 微服务时代之网关相关技术选型及部署(nacos+gateway)

    1.场景描述 因要用到微服务,关于注册中心这块,与同事在技术原型上做了讨论,初步定的方案是使用:阿里巴巴的nacos+springcloud gateway,下面表格是同事整理的注册中心对比,以前用的 ...

  3. poj2396 Budget 上下界可行流

    Budget:http://poj.org/problem?id=2396 题意: 给定一个棋盘,给定每一行每一列的和,还有每个点的性质.求一个合理的棋盘数值放置方式. 思路: 比较经典的网络流模型, ...

  4. hdu 6092 Rikka with Subset(多重背包)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6092 #include <cstdio> #include <iostream> ...

  5. Treasure Hunt CodeForces - 979B

    After the big birthday party, Katie still wanted Shiro to have some more fun. Later, she came up wit ...

  6. Object和Objects

    Object 在Java,Object类是超级父类,是所有类的父类. public boolean equals(Object obj) { return (this == obj); } publi ...

  7. 008 Python基本语法元素小结

    目录 一.概要 二.保留字 三.温度转换 一.概要 缩进.注释.命名.变量.保留字 数据类型.字符串. 整数.浮点数.列表 赋值语句.分支语句.函数 input().print().eval(). p ...

  8. MyBatis的发展和选型

    xlecho编辑整理,欢迎转载,转载请声明文章来源.欢迎添加echo微信(微信号:t2421499075)交流学习. 百战不败,依不自称常胜,百败不颓,依能奋力前行.--这才是真正的堪称强大!! 参考 ...

  9. 实验吧CTF练习题---WEB---FALSE解析

    实验吧web之FALSE 地址:http://www.shiyanbar.com/ctf/1787 flag值:CTF{t3st_th3_Sha1}   解题步骤: 1.点开题目链接,观察题意 2.题 ...

  10. 记录一次oracle的坑

    背景:程序正常运行中,突然技术支持人员反映数据库数据好久没有增加,于是乎各种排查问题,但是一直没有找到原因,由于代码比较久,也不是本人所写,更气的是居然用的是oracle数据库,并且是通过java代码 ...