逆波兰表达式

article/2025/9/19 10:30:33

逆波兰表达式在维基百科上的解释:
逆波兰表示法Reverse Polish notationRPN,或逆波兰记法),是一种是由波兰数学家扬·武卡谢维奇1920年引入的数学表达式方式,在逆波兰记法中,所有操作符置于操作数的后面,因此也被称为后缀表示法。逆波兰记法不需要括号来标识操作符的优先级。

我们中学学习的表达式叫中缀表达式例如:4+(2*5)-6

逆波兰表达式叫后缀表达式,操作符在操作数之后如:5 + ((1 + 2) * 4) − 3的逆波兰表达式 5 1 2 + 4 * + 3 −

我们人感觉中缀表达式比较好理解,但是计算机运行时调用的是堆栈,逆波兰表达式就是通过堆栈实现的,所以在计算机内部要把我们的中缀表达式转换成后缀表达式再进行运算。

我们来看看中缀表达式怎么样变成后缀表达式:

见到数字直接输出,见到符号按一定规则入栈出栈。

规则就是,用当前的符号与栈顶的符号比较优先级,如果当前符号优先级小于栈顶符号的优先级,则把栈里面的符号都弹出来。括号的操作除外,括号是左括号优先级最高,不管跟啥比,都是入栈;右括号是优先级最低,跟啥比都是弹出栈内符号,但只弹到跟它匹配的最近的那个左括号。

在这里我们需要知道符号运算优先级的问题:

这里我们要记住一句话:当前符号优先级大于栈顶符号优先级时,不弹出直接压入;小于等于时,弹出再压入。

下面我们来举个例子具体看看怎么使用栈将中缀表达式转换成后缀表达式。

5 + ((1 + 2) * 4) − 3

首先输出“5”,下一个运算符为+所以要压入栈中

 

紧接着有两个((,因为左括号优先级非常高,所以我们直接压入栈。

后面为一个1,所以直接输出。

1后面为一个+,虽然+比栈顶元素”( “低但是”( “只有遇到“ )”才会出栈,所以需要把+压栈。

后面的2直接输出。

 

2后面有“ )”这时我们就需要出栈了。

 

现在的栈顶为“( ”优先级比后面的“  *  ”高所以我们需要压栈。紧接着输出4.

 

 

后面就到了第二个“  )  ”,优先级比较低,所以我们要出栈。

下面的“  -  ”和“  +  ”优先级相同所以要先压栈再出栈。

 

最后输出3,再将-出栈。所以换算成后缀表达式为

5 1 2  +  4 *  + 3 -

维基百科C++程序实现:

#include <cstring>
#include <cstdio>// 操作符
// 优先级		符号		运算顺序
// 1		!		从右至左
// 2		* / %		从左至右
// 3		+ -		从左至右
// 4		=		从右至左
int op_preced(const char c)
{switch(c)    {case '!':return 4;case '*':  case '/': case '%':return 3;case '+': case '-':return 2;case '=':return 1;}return 0;
}unsigned int op_arg_count(const char c)
{switch(c)  {case '*': case '/': case '%': case '+': case '-': case '=':return 2;case '!':return 1;default:return c - 'A';}return 0;
}#define op_left_assoc(c) (c == '+' || c == '-' || c == '/' || c == '*' || c == '%')
#define is_operator(c)   (c == '+' || c == '-' || c == '/' || c == '*' || c == '!' || c == '%' || c == '=')
#define is_function(c)   (c >= 'A' && c <= 'Z')
#define is_ident(c)      ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z'))bool shunting_yard(const char *input, char *output)
{const char *strpos = input, *strend = input + strlen(input);char c, stack[32], sc, *outpos = output;unsigned int sl = 0;while(strpos < strend)   {c = *strpos;if(c != ' ')    {// 如果输入为一个数字,则直接入输出队列if(is_ident(c))  {*outpos = c; ++outpos;}// 如果输入为一个函数记号,则压入堆栈else if(is_function(c))   {stack[sl] = c;++sl;}// 如果输入为函数分割符(如:逗号)else if(c == ',')   {bool pe = false;while(sl > 0)   {sc = stack[sl - 1];if(sc == '(')  {pe = true;break;}else  {// 直到栈顶元素是一个左括号// 从堆栈中弹出元素入输出队列*outpos = sc; ++outpos;sl--;}}// 如果没有遇到左括号,则有可能是符号放错或者不匹配if(!pe)   {printf("Error: separator or parentheses mismatched\n");return false;}}// 如果输入符号为操作符,op1,然后:else if(is_operator(c))  {while(sl > 0)    {sc = stack[sl - 1];// While there is an operator token, o2, at the top of the stack// op1 is left-associative and its precedence is less than or equal to that of op2,// or op1 is right-associative and its precedence is less than that of op2,if(is_operator(sc) &&((op_left_assoc(c) && (op_preced(c) <= op_preced(sc))) ||(!op_left_assoc(c) && (op_preced(c) < op_preced(sc)))))   {// Pop o2 off the stack, onto the output queue;*outpos = sc; ++outpos;sl--;}else   {break;}}// push op1 onto the stack.stack[sl] = c;++sl;}// If the token is a left parenthesis, then push it onto the stack.else if(c == '(')   {stack[sl] = c;++sl;}// If the token is a right parenthesis:else if(c == ')')    {bool pe = false;// Until the token at the top of the stack is a left parenthesis,// pop operators off the stack onto the output queuewhile(sl > 0)     {sc = stack[sl - 1];if(sc == '(')    {pe = true;break;}else  {*outpos = sc; ++outpos;sl--;}}// If the stack runs out without finding a left parenthesis, then there are mismatched parentheses.if(!pe)  {printf("Error: parentheses mismatched\n");return false;}// Pop the left parenthesis from the stack, but not onto the output queue.sl--;// If the token at the top of the stack is a function token, pop it onto the output queue.if(sl > 0)   {sc = stack[sl - 1];if(is_function(sc))   {*outpos = sc; ++outpos;sl--;}}}else  {printf("Unknown token %c\n", c);return false; // Unknown token}}++strpos;}// When there are no more tokens to read:// While there are still operator tokens in the stack:while(sl > 0)  {sc = stack[sl - 1];if(sc == '(' || sc == ')')   {printf("Error: parentheses mismatched\n");return false;}*outpos = sc; ++outpos;--sl;}*outpos = 0; // Null terminatorreturn true;
}bool execution_order(const char *input) {printf("order: (arguments in reverse order)\n");const char *strpos = input, *strend = input + strlen(input);char c, res[4];unsigned int sl = 0, sc, stack[32], rn = 0;// While there are input tokens leftwhile(strpos < strend)  {// Read the next token from input.c = *strpos;// If the token is a value or identifierif(is_ident(c))    {// Push it onto the stack.stack[sl] = c;++sl;}// Otherwise, the token is an operator  (operator here includes both operators, and functions).else if(is_operator(c) || is_function(c))    {sprintf(res, "_%02d", rn);printf("%s = ", res);++rn;// It is known a priori that the operator takes n arguments.unsigned int nargs = op_arg_count(c);// If there are fewer than n values on the stackif(sl < nargs) {// (Error) The user has not input sufficient values in the expression.return false;}// Else, Pop the top n values from the stack.// Evaluate the operator, with the values as arguments.if(is_function(c)) {printf("%c(", c);while(nargs > 0)	{sc = stack[sl - 1];sl--;if(nargs > 1)	{printf("%s, ", &sc);}else {printf("%s)\n", &sc);}--nargs;}}else	{if(nargs == 1) {sc = stack[sl - 1];sl--;printf("%c %s;\n", c, &sc);}else	{sc = stack[sl - 1];sl--;printf("%s %c ", &sc, c);sc = stack[sl - 1];sl--;printf("%s;\n",&sc);}}// Push the returned results, if any, back onto the stack.stack[sl] = *(unsigned int*)res;++sl;}++strpos;}// If there is only one value in the stack// That value is the result of the calculation.if(sl == 1) {sc = stack[sl - 1];sl--;printf("%s is a result\n", &sc);return true;}// If there are more values in the stack// (Error) The user input has too many values.return false;
}int main() {// functions: A() B(a) C(a, b), D(a, b, c) ...// identifiers: 0 1 2 3 ... and a b c d e ...// operators: = - + / * % !const char *input = "a = D(f - b * c + d, !e, g)";char output[128];printf("input: %s\n", input);if(shunting_yard(input, output))    {printf("output: %s\n", output);if(!execution_order(output))printf("\nInvalid input\n");}return 0;
}

 


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

相关文章

【数据结构】-------逆波兰表达式(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. 同微信客户端一致的视觉效果…

微信网页开发(10)--扫一扫功能接口

点此查看 微信公众号/微信网页/微信支付/企业微信/小程序开发合集及源代码下载 本文目录 1. 背景2. 代码3. 测试 1. 背景 我们可以在微信网页调起扫一扫功能&#xff0c;可以识别一维码、二维码的内容&#xff0c;然后根据扫码结果实现我们的业务逻辑。 2. 代码 代码如下&am…

微信公众号开发—通过网页授权实现业务系统登录及用户绑定(微信网页授权自动登录业务系统)

&#x1f60a; 作者&#xff1a; 一恍过去 &#x1f496; 主页&#xff1a; https://blog.csdn.net/zhuocailing3390 &#x1f38a; 社区&#xff1a; Java技术栈交流 &#x1f389; 主题&#xff1a; 微信公众号开发—通过网页授权实现业务系统登录及用户绑定(微信网页授权…

微信网页开发,禁止右上角微信复制分享链接JS

禁止微信右上角分享链接 开发网页时&#xff0c;为了提高网页链接的安全&#xff0c;不想让别人分享链接给别人 一般微信打开网页后&#xff0c;点击右上角是这样的 想要网页不能被复制&#xff0c;不能分享给其他人 效果图&#xff1a; 资源文件下载地址 下载地址 直接贴…

开发微信网页及调试方法

参考资料 Mac中怎么使用Nginx实现80端口转发8080端口 - 大数据 - 亿速云 使用代理映射解决微信页面调试难题 | 梦翼坊 微信开发工具里的域名必须是在微信公众号白名单里的域名,而npm run dev大多是localhost,所以为了方便调试,需要如下步骤 在Charles勾选Proxy-macOS Proxy…

微信网页开发之JS-SDK初使用

最近需要做一个页面&#xff0c;该页面使用微信浏览器打开&#xff0c;功能如下&#xff1a; 1、用户打开链接之后获取到用户的openId&#xff0c;用于支付、获取后台数据等场景 2、自定义分享链接、标题、图标、描述等 3、隐藏微信页面中的某些菜单项列表 阅读本文前需掌握…

微信网页开发--获取微信用户信息

流程 用户扫码或者直接点击链接进入我们的入口页面&#xff1b;进入授权登录页面&#xff0c;用户点击授权登录按钮&#xff1b;微信会自动将我么的网页授权域名后增加参数&#xff1b;根据微信给的code去获取当前登录的微信用户的用户信息。 具体操作过程 1.配置网页授权域名…

微信网页开发(4)--使用JSSDK基础接口

点此查看 微信公众号/微信网页/微信支付/企业微信/小程序开发合集及源代码下载 本文目录 1. JSSDK接口2. 基础接口3. 开发流程3.1 绑定域名3.2 引入JS文件3.3 通过config接口注入权限验证配置3.5 调用基础接口 4. 小结 1. JSSDK接口 微信提供了很多JSSDK接口&#xff0c;包括基…

微信网页开发(8)--地理位置接口

点此查看 微信公众号/微信网页/微信支付/企业微信/小程序开发合集及源代码下载 本文目录 1. 背景2. 代码3. 测试 1. 背景 微信网页提供了获取当前地理位置经纬度&#xff0c;以及通过内置地图查看当前位置的接口。 官方接口说明如下&#xff1a; // 获取位置 wx.getLocation…

微信公众号开发——2、微信网页开发

第一部分、为公众号菜单嵌入网页 一、关键参考文档 微信JS-SDK说明文档 。 二、编辑模式嵌入网页 在公众号平台下&#xff0c;自定义菜单&#xff0c;添加菜单&#xff0c;并选择菜单内容跳转到指定页面地址即可&#xff08;需认证后方可添加页面地址&#xff0c;个人账号暂不…

微信网页开发配置整理

真是人越大记忆越差&#xff0c;断断续续做了几个微信小程序和微信h5项目&#xff0c;然鹅小程序的开发流程有些都忘了&#xff0c;最近刚做完一个微信h5网页开发的项目&#xff0c;所以赶紧记录下开发中需要注意的点&#xff0c;给自己往后回顾&#xff0c;同时也给第一次接入…

微信网页开发调试的一些方法

1. 微信开发者工具调试 微信开发者工具下载 不过多介绍, 下载安装, 就能开始小程序开发和公众号网页开发. 2. 微信内自带调试 微信内打开网页 http://debugx5.qq.com (仅支持Android微信) 打开下面两项&#xff0c;就可以调试了 3. 使用vConsole插件 1. 下载vConsole插件…

前端对接微信公众号网页开发流程,前期配置

微信公众号网页开发&#xff0c;其实就是我们开发的h5网页需要放到微信浏览器环境中使用&#xff0c;但是需要对接公众号授权&#xff0c;授权之后可以获取到用户的个人信息&#xff0c;以及可以使用公众号提供的一些API,如&#xff1a;图片上传、图片预览、获取位置信息、微信…

微信网页开发(3)--微信网页授权

点此查看 微信公众号/微信网页/微信支付/企业微信/小程序开发合集及源代码下载 本文目录 1. 什么是授权2. 两种授权方式3. 网页授权access_token和普通access_token4. 网页授权流程5. 网页授权代码开发5.1 项目搭建5.2 修改配置文件5.3 开发启动类5.4 开发公众号配置类5.5 开发…