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

article/2025/9/19 9:11:25

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

今天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 * -

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


http://chatgpt.dhexx.cn/article/F0m4hwvl.shtml

相关文章

逆波兰表达式,走一波

逆波兰表达式又叫后缀表达式&#xff0c;中学时候学的那种表达式叫中缀表达式。 例如&#xff0c;5(63)3-1 &#xff0c; 3(4(21)2)-3 例子中的这两个式子&#xff0c;就是中缀表达式。下面这两个就是后缀表达式&#xff1a; 56331- &#xff0c; 342123- 中缀…

计算逆波兰表达式

⭐作者介绍&#xff1a;大二本科网络工程专业在读&#xff0c;持续学习Java&#xff0c;努力输出优质文章 ⭐作者主页&#xff1a;逐梦苍穹 ⭐所属专栏&#xff1a;数据结构。数据结构专栏主要是在讲解原理的基础上拿Java实现 ⭐码云地址超链接(Gitee)&#xff1a;这里存放我学…

JavaScript逆波兰表达式求值

逆波兰表达式简介 逆波兰表达式又叫做后缀表达式。逆波兰表示法是波兰逻辑学家J・卢卡西维兹(J・ Lukasiewicz)于1929年首先提出的一种表达式的表示方法 。后来,人们就把用这种表示法写出的表达式称作“逆波兰表达式”。逆波兰表达式把运算量写在前面,把算符写在后面。 逻辑提…

【题解】 逆波兰表达式

今天随机选题选到了这道题&#xff0c;这题比较经典&#xff0c;包含的知识点也不少&#xff0c;就在此与大家分享一下我的思路。 原题目&#xff1a;MFOJ P753-逆波兰表达式​​​​​ 题目描述 逆波兰记法中&#xff0c;操作符置于操作数的后面。例如表达“三加四”时&…

【JAVA】---逆波兰表达式

一. 逆波兰表达式的介绍 逆波兰表达式又称为后缀表达式&#xff0c;代表的含义是操作数在前&#xff0c;运算符在后。 比如&#xff1a;12&#xff0c;用逆波兰表达式来写的话&#xff0c;就是12。 而12这种写法称为中缀表达式&#xff0c;即运算符在两个操作数之间&#xff0c…

逆波兰表达式题解

1696:逆波兰表达式 总时间限制: 1000ms 内存限制: 65536kB 描述 逆波兰表达式是一种把运算符前置的算术表达式&#xff0c;例如普通的表达式2 3的逆波兰表示法为 2 3。逆波兰表达式的优点是运算符之间不必有优先级关系&#xff0c;也不必用括号改变运算次序&#xff0c…

C++题解 | 逆波兰表达式相关

✨个人主页&#xff1a; 北 海 &#x1f389;所属专栏&#xff1a; C/C相关题解 &#x1f38a;每篇一句&#xff1a; 图片来源 A year from now you may wish you had started today. 明年今日&#xff0c;你会希望此时此刻的自己已经开始行动了。 文章目录 &#x1f307;前言…

(详细图解) 逆波兰表达式

下面给出图解: 下面给出代码: class Solution { public:int evalRPN(vector<string>& tokens) {stack<int> st;//循环遍历表达式 范围forfor(const auto& str : tokens) {if(str "" || str "-"|| str "*" || str &quo…

详解逆波兰表达式的转换与表达式求值

对于计算一个算式 如 : 3*(56)-2 这种算式叫做中缀表达式, 人们看着会比较方便, 如果用计算机直接计算会很麻烦,所以要把中缀表达式变为计算机易于理解的后缀表达式来计算. 后缀表达式又叫逆波兰表达式, 把运算量写在前面, 把运算符写在后面, 并且可以去掉括号 如 ab 变为 …

逆波兰表达式(后缀表达式)C++实现

1 何谓逆波兰表达式 逆波兰表达式又称为后缀表达式&#xff0c;是波兰逻辑学家J・卢卡西维兹(J・ Lukasewicz)于1929年首先提出的一种表达式的表示方法。 例如&#xff1a; 1 2 3&#xff0c;转换为逆波兰表达式&#xff1a;1 2 3 。 1 2 * 3&#xff0c;转换为逆波兰表达…

将一般算术表达式转化为逆波兰表达式,并求逆波兰表达式的值。

要求&#xff1a;设计一个算法&#xff0c;将一般算术表达式转化为逆波兰表达式&#xff0c;并求逆波兰表达式的值。 实现思路 获取一个中缀表达式将表达式转换为后缀表达式计算后缀表达式的结果 中缀表达式转换为后缀表达式的几个关键部分 假如不是运算符&#xff0c;则输…

波兰表达式与逆波兰表达式

文章目录 波兰表达式逆波兰表达式波兰表达式计算逆波兰表达式计算总结 常见的算术表达式&#xff0c;称为中缀表达式&#xff0c;例如&#xff1a; 5 ( 6 – 4 / 2 ) * 3波兰表达式 波兰表达式也称为前缀表达式&#xff0c;以上面的例子为例&#xff0c;其波兰表达式为&…

逆波兰表达式

逆波兰表达式在维基百科上的解释&#xff1a;逆波兰表示法&#xff08;Reverse Polish notation&#xff0c;RPN&#xff0c;或逆波兰记法&#xff09;&#xff0c;是一种是由波兰数学家扬武卡谢维奇1920年引入的数学表达式方式&#xff0c;在逆波兰记法中&#xff0c;所有操作…

【数据结构】-------逆波兰表达式(C++)

文章目录 逆波兰表达式讲解正常表达式转换到逆波兰表达式栈操作逆波兰表达式的原理多位数压入栈操作代码例题 逆波兰表达式讲解 逆波兰表达式-----是数据结构的应用&#xff0c;你要单独说讨论它的话没有多大意义&#xff0c;如果我们结合数据结构中的栈来讲解的话&#xff0c…

什么是逆波兰表达式?

文章目录 1. 题目描述2. 解题思路3. 动图演示4. 代码实现 1. 题目描述 2. 解题思路 逆波兰表达式由波兰的逻辑学家卢卡西维兹提出&#xff0c;它的特点是&#xff1a;没有括号&#xff0c;运算符总是放在和它相关的操作数之后。因此&#xff0c;逆波兰表达式也称后缀表达式&am…

微信网页开发中授权headimgurl有的为空

微信网页开发授权&#xff0c;有的用户头像链接为空。 微信开发文档&#xff1a;https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html headimgurl用户头像&#xff0c;最后一个数值代表正方形头像大小&#xff08;有0、46、64、…

微信开发——网页授权

微信开发——网页授权 前期准备前端后端 前期准备 ①微信客户端中访问第三方页面&#xff0c;公众号可以通过网页登陆授权&#xff0c;获取微信用户的基本信息&#xff08;头像、昵称等&#xff09;&#xff0c;实现业务逻辑。一切按照官方文档说明开发。 ②安装微信开发者工具…

微信公众号微信网页开发网页授权/回调自定义参数问题处理方法。

微信公众号页面授权回调自定义参数问题 我们知道微信页面回调接口&#xff0c;获得用户信息后&#xff0c;回调地址&#xff1a; redirect_uri&#xff1a;授权后重定向的回调链接地址&#xff0c; 请使用 urlEncode 对链接进行处理。 授权后跳转回页面&#xff1a; redirec…

微信网页开发--分享接口

流程 关于流程&#xff0c;在上一篇中已经有图介绍: 微信文档 微信JS-SDK说明文档 JSSDK使用步骤 首先确保已经获取了相关权限 步骤一&#xff1a;绑定域名 先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。 备注&#xff1a;登录后…

微信网页开发样式库的使用

一、WeUI 是什么 WeUI 是一套同微信原生视觉体验一致的基础样式库&#xff0c;由微信官方设计团队为微信内网页和微信小程序量身设计&#xff0c;令用户的使用感知更加统一。在微信网页或小程序中使用 WeUI&#xff0c;有如下优势&#xff1a; 1. 同微信客户端一致的视觉效果…