http://www.cnblogs.com/stay-foolish/archive/2012/04/25/2470590.html

假设表达式由单字母变量和双目四则运
算算符构成。试写一个算法,将一个通常书写形式
且书写正确的表达式转换为逆波兰式。

实现下列函数:
char *RPExpression(char *e);
/* 返回表达式e的逆波兰式 */

Stack是一个已实现的栈。
可使用的相关类型和函数:
typedef char SElemType; // 栈Stack的元素类型
Status InitStack(Stack &s);
Status Push(Stack &s, SElemType e);
Status Pop(Stack &s, SElemType &e);
Status StackEmpty(Stack s);
SElemType Top(Stack s);

-------------------------------------------------------------------------------------------------

二、思路

  拿到题目,要做的第一件事情,就是搞懂题目究竟要我们做什么,很显然,题目中的关键字是“逆波兰式”,那么首先我们要搞懂这个概念。

  所谓的逆波兰表示法Reverse Polish notationRPN,或逆波兰记法),是一种数学表达式方式,在逆波兰记法中,所有操作符置于操作数的后面,因此也被称为后缀表示法。逆波兰记法不需要括号来标识操作符的优先级。(摘自维基)

  举个简单的例子,平常我们写的数学表达式a+b,就是一种中缀表达式,写成后缀表达式就是ab+。再举一个复杂的例子,中缀表达式(a+b)*c-(a+b)/e的逆波兰式是ab+c*ab+e/-。

  在弄清楚概念以及题目的要求之后,接下来就要编写算法了。那么将一个表达式转换为逆波兰式的算法思想是什么呢?

  (1)首先,需要分配2个栈,栈s1用于临时存储运算符(含一个结束符号),此运算符在栈内遵循越往栈顶优先级越高的原则;栈s2用于输入逆波兰式,为方便起见,栈s1需先放入一个优先级最低的运算符,在这里假定为'#';

  (2)从中缀式的左端开始逐个读取字符x,逐序进行如下步骤:

      1.若x是操作数,则分析出完整的运算数(在这里为方便,用字母代替数字),将x直接压入栈s2;

      2.若x是运算符,则分情况讨论:

          若x是'(',则直接压入栈s1;

          若x是')',则将距离栈s1栈顶的最近的'('之间的运算符,逐个出栈,依次压入栈s2,此时抛弃'(';

          若x是除'('和')'外的运算符,则再分如下情况讨论:

              若当前栈s1的栈顶元素为'(',则将x直接压入栈s1;

              若当前栈s1的栈顶元素不为'(',则将x与栈s1的栈顶元素比较,若x的优先级大于栈s1栈顶运算符优先级,则将x直接压入栈s1。否者,将栈s1的栈顶运算符弹出,压入栈s2中,直到栈s1的栈顶运算符优先级别低于(不包括等于)x的优先级,或栈s2的栈顶运算符为'(',此时再则将x压入栈s1;

【第二步是一个循环,要把中缀式读完。第三步是在循环之外】

  (3)在进行完(2)后,检查栈s1是否为空,若不为空,则将栈中元素依次弹出并压入栈s2中(不包括'#');      

  

  (4)完成上述步骤后,栈s2便为逆波兰式输出结果。但是栈s2应做一下逆序处理,因为此时表达式的首字符位于栈底;

-------------------------------------------------------------------------------------------------

大家可以用上述算法计算 x=d-a*(e+c)/f

后缀表达式是:xdaec+*f/-=

有一个快速检测是否写错的方法:

后缀表达式的运算的对象的顺序和中缀式对象的顺序是相同的

三、代码(C/C++)

C代码

 1 char *RPExpression(char *e)
 2 /* 返回表达式e的逆波兰式 */
 3 {
 4     //栈s1用于存放运算符,栈s2用于存放逆波兰式
 5     Stack s1,s2;
 6     InitStack(s1);
 7     InitStack(s2);
 8
 9     //假设字符'#'是运算级别最低的运算符,并压入栈s1中
10     Push(s1,'#');
11
12     //p指针用于遍历传入的字符串,ch用于临时存放字符,length用于计算字符串长度
13     char *p=e,ch;
14     int length=0;
15     for(;*p!='\0';p++)//逐个字符访问
16     {
17         switch(*p)
18         {
19             //遇'('则直接入栈s1
20             case '(':
21                 Push(s1,*p);
22                 break;
23             //遇')'则将距离栈s1栈顶的最近的'('之间的运算符,逐个出栈,依次送入栈s2,此时抛弃'('
24             case ')':
25                 while(Top(s1)!='(')
26                 {
27                     Pop(s1,ch);
28                     Push(s2,ch);
29                 }
30                 Pop(s1,ch);
31                 break;
32             //遇下列运算符,则分情况讨论:
33             //1.若当前栈s1的栈顶元素是'(',则当前运算符直接压入栈s1;
34             //2.否则,将当前运算符与栈s1的栈顶元素比较,若优先级较栈顶元素大,则直接压入栈s1中,
35             //  否则将s1栈顶元素弹出,并压入栈s2中,直到栈顶运算符的优先级别低于当前运算符,然后再将当前运算符压入栈s1中
36             case '+':
37             case '-':
38                 for(ch=Top(s1);ch!='#';ch=Top(s1))
39                 {
40                     if(ch=='(')
41                     {
42                         break;
43                     }
44                     else
45                     {
46                         Pop(s1,ch);
47                         Push(s2,ch);
48                     }
49                 }
50                 Push(s1,*p);
51                 length++;
52                 break;
53             case '*':
54             case '/':
55                 for(ch=Top(s1);ch!='#'&&ch!='+'&&ch!='-';ch=Top(s1))
56                 {
57                     if(ch=='(')
58                     {
59                         break;
60                     }
61                     else
62                     {
63                         Pop(s1,ch);
64                         Push(s2,ch);
65                     }
66                 }
67                 Push(s1,*p);
68                 length++;
69                 break;
70             //遇操作数则直接压入栈s2中
71             default:
72                 Push(s2,*p);
73                 length++;
74         }
75     }
76     //若栈s1非空,则将栈中元素依次弹出并压入栈s2中
77      while(!StackEmpty(s1)&&Top(s1)!='#')
78     {
79         Pop(s1,ch);
80         Push(s2,ch);
81     }
82     //最后将栈s2输出,逆序排列成字符串;
83     char *result;
84     result=(char *)malloc(sizeof(char)*(length+1));
85     result+=length;
86     *result='\0';
87     result--;
88     for(;!StackEmpty(s2);result--)
89     {
90         Pop(s2,ch);
91         *result=ch;
92     }
93     ++result;
94     return result;
95 }

-------------------------------------------------------------------------------------------------

四、总结

  对于实现逆波兰式算法,一开始不懂得概念的时候的确不知道如何入手,在摸清思路后,其实难度并不大,关键在于逻辑要清晰,而且要细心,写这段代码的时候很痛苦,共用了两天的时间(真的好菜)。

  另摘录维基及度娘中关于实现逆波兰式的意义:(摘自百度)

    为什么要将看似简单的中序表达式转换为复杂的逆波兰式?原因就在于这个简单是相对人类的思维结构来说的,对计算机而言中序表达式是非常复杂的结构。相对的,逆波兰式在计算机看来却是比较简单易懂的结构。因为计算机普遍采用的内存结构是栈式结构,它执行先进后出的顺序。    

  

  逆波兰式的意义:(摘自维基)

    当有操作符时就计算,因此表达式并不是从右至左整体计算而是每次由中心向外计算一部分,这样在复杂运算中就很少导致操作符错误。

    堆栈自动记录中间结果,这就是为什么逆波兰计算器能容易对任意复杂的表达式求值。与普通科学计算器不同,它对表达式的复杂性没有限制。

    逆波兰表达式中不需要括号,用户只需按照表达式顺序求值,让堆栈自动记录中间结果;同样的,也不需要指定操作符的优先级。

    逆波兰计算器中,没有“等号”键用于开始计算。

    逆波兰计算器需要“确认”键用于区分两个相邻的操作数。

    机器状态永远是一个堆栈状态,堆栈里是需要运算的操作数,栈内不会有操作符。

    教育意义上,逆波兰计算器的使用者必须懂得要计算的表达式的含义。

问题2:如何求出逆波兰式?

下面以(a+b)*c为例子进行说明:

  (a+b)*c的逆波兰式为ab+c*,假设计算机把ab+c*按从左到右的顺序压入栈中,并且按照遇到运算符就把栈顶两个元素出栈,执行运算,得到的结果再入栈的原则来进行处理,那么ab+c*的执行结果如下:   1)a入栈(0位置)   2)b入栈(1位置)   3)遇到运算符“+”,将a和b出栈,执行a+b的操作,得到结果d=a+b,再将d入栈(0位置)   4)c入栈(1位置)   5)遇到运算符“*”,将d和c出栈,执行d*c的操作,得到结果e,再将e入栈(0位置)   经过以上运算,计算机就可以得到(a+b)*c的运算结果e了。

将表达式转换成逆波兰式相关推荐

  1. 中缀表达式转换成逆波兰式

    栈的应用:中缀表达式转换成逆波兰式 小白前来报道!懒癌患者的第一篇博客,实属不易,先为自己鼓个掌. 编写程序,将任意一个合法的中缀表达式转换成逆波兰式. [问题描述]表达式计算是实现程序设计语言的基本 ...

  2. php逆波兰表达式,PHP根据数字的字符表达式计算出结果(转换成逆波兰式再求解)[转]...

    这个简单的计算器采用的是逆波兰式来做的,仅支持加减乘除四种运算,纯粹个人练习记录一下,还望多多支持. 用法 require 'Calc.php'; $calc = new Calc('(1+9)/2' ...

  3. php逆波兰表达式,PHP实现逆波兰式 - 计算工资时用

    近期一个小项目需要用到公式运算, 所以就进行一些了解,以下内容均属于个人经验. 在PHP中实现公式表达式四则运算大概有两种方法: 1)使用系统函数eval 2)将表达式转换成逆波兰表达式进行计算. / ...

  4. 【Python】洛谷 P1175_表达式的转换(逆波兰式、中缀表达式、后缀表达式、栈)

    目录 题目 代码 AC截图 题目 代码 碎碎念:我用来复习栈的,刷了巨长时间,一直Runtine Error,编译直接就没有过. 好家伙,然后发现是数据给的不够严谨,左右两端有空格,使用strip() ...

  5. java逆波兰式求值_波兰式、逆波兰式与表达式求值

    波兰式.逆波兰式是<数据结构>课程中讲解关于栈的时候提到的,栈是很简单的一种数据结构.但是这些理论的提出却是计算机早期发展领域的重大突破,值得仔细回味. 1. 中缀表达式 我们在数学中学到 ...

  6. 编译原理 —— 逆波兰式

    什么是逆波兰式 逆波兰式除去了原表达式中的括号,并将运算对象写在前面,运算符写在后面,因而又称为后缀式.用逆波兰式表示表达式的最大优点是易于计算处理. 逆波兰式处理过程 逆波兰式只使用一个工作栈,当计 ...

  7. 编译原理实验二-逆波兰式生成程序

    一.实验目的和要求: 1. 掌握语法分析的基本思想,并用高级语言编写逆波兰式生成程序 2. 要求利用逆波兰式生成算法编写程序,将从键盘上输入的算术表达式 (中缀表达式)转化成逆波兰式 二.实验平台: ...

  8. 波兰式与逆波兰式的转换和表达式求值

    文章目录 一.前言 二.表达式 1.中缀表达式 1.1 定义 2.前缀表达式 2.1 定义 2.2 求值 3.后缀表达式 3.1 定义 3.2 求值 三.表达式转换 1.中缀表达式转换成后缀表达式 1 ...

  9. 波兰式、逆波兰式与表达式求值

    波兰式.逆波兰式与表达式求值 <数据结构>中关于栈的解释经常会涉及到逆波兰式,波兰式,中缀式表达式的求值问题.但是,十分惭愧,整个大一阶段, 数据结构的课程没有上够5节,没有意识要学习,吃 ...

最新文章

  1. cocos2d-x ios游戏开发初认识(六) 渲染的优化
  2. qt git linux 安装,git – 如何在Ubuntu上安装QtWebEngine
  3. python学习总结----时间模块 and 虚拟环境(了解)
  4. JS之返回字符串最后出现的位置lastIndexOf
  5. 【POJ - 1661】Help Jimmy(记忆化搜索,dp)
  6. 关于c# SESSION丢失问题解决办法
  7. api-ms-win-crt-process-l1-1-0.dll 丢失的处理,遇到问题和完美解决
  8. 控制台应用程序中Main函数的args参数
  9. info - 阅读 info 文档
  10. Java 11 的新特性(上)
  11. ubunt Linux nginx,linux ubuntu系统安装nginx教程
  12. input输入框自动去除空格
  13. psm倾向得分匹配法举例_倾向得分匹配(PSM)操作过程与问题反思
  14. 用计算器算以2为底的对数
  15. Effective TCP/IP Programming读书笔记
  16. 准大二生的暑期自学安排
  17. 转载:做人开心最重要
  18. 有人知道这是错哪了么?
  19. IDM关于某些应用程序阻止了IDM集成到浏览器中
  20. python对erp系统有帮助吗_ERP系统的优点

热门文章

  1. 如何构造天然满足某些约束的神经网络?
  2. 天池大赛通用目标检测的对抗攻击方法一览
  3. 更深的编码器+更浅的解码器=更快的自回归模型
  4. Designing GANs:又一个GAN生产车间
  5. HDU1426 Sudoku Killer DFS
  6. linux vnc的小黑点和鼠标不同步_vnc使用教程,在Linux中vnc使用教程的4个步骤
  7. fastjson safemode_它又又又来了,Fastjson 最新高危漏洞来袭!
  8. Java中的List接口实现类LinkedList
  9. springboot配置templates直接访问
  10. LeetCode 8 字符串转换整数 (atoi)