波兰表达式和逆波兰表达式

今天zxy的实验内容是关于逆波兰表达式的计算,刚好最近在做关于数据结构的习题,于是想着对波兰表达式和逆波兰表达式的转化和运算分别进行一个学习,于是写了这篇博客(有错的地方欢迎大家指出。)

常见的运算表达式,我们一般称为中缀表达式,例如:

5 + ( 6 - 4 / 2 ) * 3

波兰表达式

波兰表达式子我们也称作前缀表达式,就是对中缀表达式进行如下的操作:

首先建立两个栈,一个栈s1用于落实我们最后得到的前缀表达式,一个栈s2用于暂存表达式中的运算符。

对中缀表达式从右向左进行遍历:

如果是数字:则直接压入栈s1。

如果是括号:分为两种情况,如果是右括号,则直接压入栈s2;如果是左括号,我们将栈s2中的运算符依次弹栈到栈s1,直到s1栈顶元素为右括号,将右括号弹栈,结束。

如果是其他运算符:将此时s2栈顶运算符的优先级与该运算符进行比较。如果s2栈顶操作符优先级大于该运算符优先级,则s2弹栈加入到栈s1中,直到s2栈顶操作符优先度小于等于该运算符优先级,将当前运算符压入栈s2中。

遍历完整个中缀表达式后,检测栈s2是否为空。如果不为空,则将s2中的运算符依次弹栈,压入栈s1中。

最后将栈s1中的元素依次弹出(反序输出),得到的即是前缀表达式。

模拟过程:

代码实现:

#include<iostream>
#include<cctype>
#include<stack>
#include<map>
using namespace std;
stack<char>s1,s2;//s1表示存放波兰表达式的栈,s2用于暂存运算符
map<char,int>ch_level;//用于建立运算符到优先级的映射
string s;
void BL(int x){//转化递归函数 if (x<0) return;//递归结束 if (isdigit(s[x])) {s1.push(s[x]); BL(x-1);}//如果是数字直接进栈s1 else if (s[x]==')'){s2.push(s[x]); BL(x-1);}//右括号直接进栈s2//遍历到左括号,将栈s2中右括号之上的运算符全部压入栈s1中//需要注意的是,由于我们可以确定此时栈中必然存在一个右括号,所以在while中不用判断栈是否为空 else if (s[x]=='('){while (s2.top()!=')'){s1.push(s2.top()); s2.pop();} s2.pop();BL(x-1);}else {//如果是其他运算符,就对优先级进行比较,这里需要判断栈是否为空 while (!s2.empty()&&ch_level[s[x]]>ch_level[s2.top()]){s1.push(s2.top()); s2.pop();}s2.push(s[x]); BL(x-1); }
}
int main(){ch_level['/']=1;ch_level['*']=1;ch_level['%']=1;//这三个的优先级比+和-高 ch_level['+']=2;ch_level['-']=2;ch_level['(']=3;ch_level[')']=3;//括号的优先级确实应该是最高的,但是我在观察流程的时候,发现括号是不参与运算符的比较的//所以我默认括号的运算符等级最低 cin>>s; BL(s.size()-1);while(!s2.empty()){s1.push(s2.top());s2.pop();}//将栈s2中剩余的元素依次压入s1中 while (!s1.empty()){cout<<s1.top()<<" ";s1.pop();}//输出结果,因为是栈所以自动进行了翻转return 0;
}

波兰表达式计算

对前缀表达式从后向前进行遍历,建立一个栈s,如果是数字,则将其直接压入栈s。

如果遍历到的是运算符,则从栈s中弹出两个数按照运算符的规则进行运算,并将计算结果压入栈s中。

合法的前缀表达式遍历结束后,栈s中剩余的元素只剩下一个,就是我们需要的结果。

代码实现:

#include<stack>
#include<iostream>
#include<cctype>
using namespace std;
int main(){string s; cin>>s;stack<int>ans; bool flag=true;//flag表示这个式子是否合法 for (int i=s.size()-1;i>=0&&flag;i--){ if (isdigit(s[i])) ans.push((int)s[i]-48);//如果是数字则直接进栈 else {//这里首先需要判断一下栈的长度是否足够弹出两个数,如果不足够说明表达式不合法 if (ans.size()<2) {flag=false; break;}int a=ans.top(); ans.pop();int b=ans.top(); ans.pop();switch (s[i]){//不同运算符的不同运算法则 case '+':ans.push(a+b);break;case '-':ans.push(a-b);break;case '*':ans.push(a*b);break;case '/':{//除法和余数需要判断除数是否为0 if (!b) flag=false; else ans.push(a/b); break;}case '%':{if (!b) flag=false; else ans.push(a%b); break;}}}}if (ans.size()!=1) flag=false;//判断栈中的值是否为1 if (flag) cout<<ans.top(); else cout<<"非法表达式";return 0;
}

我们将两个代码结合起来就可以得到一个普通算式的计算结果。


逆波兰表达式

逆波兰表达式也称后缀表达式,就是对中缀表达式进行如下操作:

对中缀表达式从左向右进行遍历:

如果是数字:则直接压入栈s1。

如果是括号:分为两种情况,如果是左括号,则直接压入栈s2;如果是右括号,我们将栈s2中的运算符依次弹栈到栈s1,直到s1栈顶元素为右括号,将右括号弹栈,结束。

如果是其他运算符:将此时s2栈顶运算符的优先级与该运算符进行比较。如果s2栈顶操作符优先级大于等于该运算符优先级,则s2弹栈加入到栈s1中,直到s2栈顶操作符优先度小于该运算符优先级,将当前运算符压入栈s2中。

遍历完整个中缀表达式后,检测栈s2是否为空。如果不为空,则将s2中的运算符依次弹栈,压入栈s1中。

最后将栈s1中的元素(正序输出),得到的即是后缀表达式。

模拟流程:

可以发现的是,后缀表达式的转化和前缀表示式只有一些细微的差别。可以说是反过来了,所以代码只需要进行很小的修改就可以了:

#include<iostream>
#include<cctype>
#include<stack>
#include<map>
using namespace std;
stack<char>s1,s2;//s1表示存放逆波兰表达式的栈,s2用于暂存运算符
map<char,int>ch_level;//用于建立运算符到优先级的映射
string s;
void NBL(int x){//转化递归函数 if (x==s.size()) return;//递归结束 if (isdigit(s[x])) {s1.push(s[x]); NBL(x+1);}//如果是数字直接进栈s1 else if (s[x]=='('){s2.push(s[x]); NBL(x+1);}//左括号直接进栈s2//遍历到右括号,将栈s2中右括号之上的运算符全部压入栈s1中//需要注意的是,由于我们可以确定此时栈中必然存在一个左括号,所以在while中不用判断栈是否为空 else if (s[x]==')'){while (s2.top()!='('){s1.push(s2.top()); s2.pop();} s2.pop();NBL(x+1);}else {//如果是其他运算符,就对优先级进行比较,这里需要判断栈是否为空 while (!s2.empty()&&ch_level[s[x]]>=ch_level[s2.top()]){s1.push(s2.top()); s2.pop();}s2.push(s[x]); NBL(x+1); }
}
int main(){ch_level['/']=1;ch_level['*']=1;ch_level['%']=1;//这三个的优先级比+和-高 ch_level['+']=2;ch_level['-']=2;ch_level['(']=3;ch_level[')']=3;//括号的优先级确实应该是最高的,但是我在观察流程的时候,发现括号是不参与运算符的比较的//所以我默认括号的运算符等级最低 cin>>s; NBL(0);char ans_c[100]; int num=0;while(!s2.empty()){s1.push(s2.top());s2.pop();}//将栈s2中剩余的元素依次压入s1中while (!s1.empty()){ans_c[num]=s1.top();s1.pop();num++;}for (int i=num-1;i>=0;i--) cout<<ans_c[i]; //正序输出,但是对于栈而言是反序 return 0;
}

逆波兰表达式的计算

对后缀表达式从前向后进行遍历,建立一个栈s,如果是数字,则将其直接压入栈s。

如果遍历到的是运算符,则从栈s中弹出两个数按照运算符的规则进行运算,并将计算结果压入栈s中。

合法的后缀表达式遍历结束后,栈s中剩余的元素只剩下一个,就是我们需要的结果。

和波兰表达式也是非常类似的,这里我将转化和计算的两个代码结合了起来:

#include<iostream>
#include<cctype>
#include<stack>
#include<map>
using namespace std;
stack<char>s1,s2;//s1表示存放逆波兰表达式的栈,s2用于暂存运算符
map<char,int>ch_level;//用于建立运算符到优先级的映射
string s;
void NBL(int x){//转化递归函数 if (x==s.size()) return;//递归结束 if (isdigit(s[x])) {s1.push(s[x]); NBL(x+1);}//如果是数字直接进栈s1 else if (s[x]=='('){s2.push(s[x]); NBL(x+1);}//左括号直接进栈s2//遍历到右括号,将栈s2中右括号之上的运算符全部压入栈s1中//需要注意的是,由于我们可以确定此时栈中必然存在一个左括号,所以在while中不用判断栈是否为空 else if (s[x]==')'){while (s2.top()!='('){s1.push(s2.top()); s2.pop();} s2.pop();NBL(x+1);}else {//如果是其他运算符,就对优先级进行比较,这里需要判断栈是否为空 while (!s2.empty()&&ch_level[s[x]]>=ch_level[s2.top()]){s1.push(s2.top()); s2.pop();}s2.push(s[x]); NBL(x+1); }
}
int main(){ch_level['/']=1;ch_level['*']=1;ch_level['%']=1;//这三个的优先级比+和-高 ch_level['+']=2;ch_level['-']=2;ch_level['(']=3;ch_level[')']=3;//括号的优先级确实应该是最高的,但是我在观察流程的时候,发现括号是不参与运算符的比较的//所以我默认括号的运算符等级最低 cin>>s; NBL(0);char ans_c[100]; int num=0;while(!s2.empty()){s1.push(s2.top());s2.pop();}//将栈s2中剩余的元素依次压入s1中while (!s1.empty()){ans_c[num]=s1.top();s1.pop();num++;}stack<int>ans; bool flag=true; for (int i=num-1;i>=0&&flag;i--){cout<<ans_c[i];if (isdigit(ans_c[i])) ans.push((int)ans_c[i]-48);//如果是数字则直接进栈else {//这里首先需要判断一下栈的长度是否足够弹出两个数,如果不足够说明表达式不合法 if (ans.size()<2) {flag=false; break;}int a=ans.top(); ans.pop();int b=ans.top(); ans.pop();switch (ans_c[i]){//不同运算符的不同运算法则 case '+':ans.push(a+b);break;case '-':ans.push(b-a);break;case '*':ans.push(a*b);break;case '/':{//除法和余数需要判断除数是否为0 if (!a) flag=false; else ans.push(b/a); break;}case '%':{if (!a) flag=false; else ans.push(b%a); break;}}}}cout<<endl;if (ans.size()!=1) flag=false;//判断栈中的值是否为1 if (flag) cout<<ans.top(); else cout<<"非法表达式";return 0;
}

结果看上去还是不错的:


查了一些资料发现可以将中缀表达式像以下的方法解析成一个树:

中缀表达式得名于它是由相应的语法树的中序遍历的结果得到的。上面的二叉树中序遍历的结果就是A+B*(C-D)-E*F。

前缀表达式是由相应的语法树的前序遍历的结果得到的。上图的前缀表达式为- + A * B - C D * E F

后缀表达式又叫做逆波兰式。它是由相应的语法树的后序遍历的结果得到的。上图的后缀表达式为:A B C D - * + E F * -

也就是说理论上可以通过树对这个问题进行处理,但是具体怎么实现我还没有想明白,有想法的欢迎大家和我交流(我知道没人会来,所以我还是去问老师)。

波兰表达式和逆波兰表达式相关推荐

  1. java中缀表达式转后缀表达式(逆波兰算法)

    四则运算是栈的重要应用之一 中缀表达式转后缀表达式(逆波兰算法)过程 从左到右遍历中缀表达式 数字直接输出为后缀表达式一部分 如果是符号,则判断与栈顶元素的优先级 高于栈顶元素优先级直接入栈 低于或等 ...

  2. 使用栈解决的一类经典问题:表达式转换及求值;中缀表达式;前缀表达式,后缀表达式,中缀转前缀;中缀转后缀;后缀表达式求值;波兰式,逆波兰式

    文章目录 背景知识 表达式转换问题(考研经典) 一:手工转换 (1)中缀转前缀和中缀转后缀 (2)前缀转中缀和后缀转中缀 二:用栈实现表达式转换 (1)中缀转后缀 (2)中缀转前缀 表达式计算问题(使 ...

  3. 前缀表达式后缀表达式_你知道波兰表达式和逆波兰表达式吗

    什么是波兰表达式 我们日常的运算表达式通常是如下形式,这种成为中缀表达式,也就是运算符在运算数的中间.这种表达式人类很容易识别,并根据其进行计算,但计算机识别这种表达式非常困难. a + b * (c ...

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

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

  5. C语言栈的运用:后缀表达式(逆波兰表达式)计算器

    原理很简单,当操作者输入中每遇数字便将数字压入数栈,每遇符号便进行两次弹栈进行该符号的运算,运算结果再次压入数栈.唯一要注意的一点就是进行除法运算时需要判断除数是否为0. 头文件: /*Captain ...

  6. c语言数据结构逆波兰算法,[分享]表达式二叉树逆波兰式的转换程序源代码(C++)...

    [分享]表达式二叉树逆波兰式的转换程序源代码(C++) RT,这是我两年前学数据结构时的作品. 拿出来给大家分享. ps:当时才学C++,代码写的不好,见笑了./*将中缀式转换为表达式树,并打印逆波兰 ...

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

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

  8. 【数据结构】波兰式、逆波兰式与中缀表达式

    目录 中缀表达式 后缀表达式 前缀表达式 计算后缀表达式结果 计算机实现中缀转后缀 中缀表达式  中缀表达式即运算符在操作数之间的表达式,常见表达式均为中缀表达式.因为中缀表达式更利于人们理解以及计算 ...

  9. 波兰式,逆波兰式,中缀表达式相互转换

    引入波兰式与逆波兰式: 一个式子,可以分成几个层面来看.比如1 + 2 * 3,我们看它是个算式,计算机看它,那就是个字符串,所以首先必须把它拆分成计算机可以操作的数据单元,就是Tokenize.比如 ...

  10. 波兰表达式与逆波兰表达式

    中缀表达式:(3+4)×5-6 波兰(前缀)表达式:- × + 3 4 5 6 逆波兰(后缀)表达式:3 4 + 5 × 6 - 波兰表达式计算规则: 1.找出"运算符.操作数.操作数&qu ...

最新文章

  1. Linux常用命令学习(shell 脚本)
  2. java 不知道键值名_java-如果您知道曲线名称和原始私钥/点,如...
  3. 《C程序员:从校园到职场》出版预告(1):从“高大上”到“柴米油盐”
  4. SQL Server之增删改操作
  5. line和spline_探索适用于Apache Spark的Spline Data Tracker和可视化工具(第1部分)
  6. docker linux 快速开窗口_技术|如何使用 Docker 快速配置数据科学开发环境?
  7. 缓存问题引发的一系列优化
  8. 互联网大厂春节礼盒鄙视链
  9. 181012词霸扇贝有道每日一句
  10. 在多GPU系统上使用hashcat进行密码破解
  11. 解决Mac下应用“已损坏”或“将对您的电脑造成伤害”
  12. Qt 之 Concurrent Run
  13. 利用H5SVG实现线性动画效果
  14. CAN 错误帧和原理
  15. maya导入arnold代理ass文件后,无法修改ass内贴图路径问题的解决方法
  16. 我的意中人是个盖世程序员.........
  17. 数据类型 - Array
  18. 服务器单独运行jar包方法
  19. c51的八位智力抢答器C语言编程,基于AT89C51单片机的八位智力抢答器设计.doc
  20. PCB走线和过孔载流问题详解

热门文章

  1. 傅里叶变换与时域频域关系
  2. 卡王。卡皇一个不为人知的密秘.必看(转)
  3. 教师资格证考试计算机知识题库,教师资格考试《高中信息技术》试题
  4. DreamWeaver CC网页设计与制作
  5. kali无线wifi密码破解
  6. HTML常用table样式
  7. Ember Model
  8. python3 数据挖掘 之 爬取 智联招聘网站来巩固pandas
  9. java数据清洗_数据清洗例子
  10. 计算机英语背诵发音,联想法巧记英语单词5000发音记忆法背单词