前缀表达式与后缀表达式求法(栈的应用)
1.前缀、中缀、后缀表达式
中缀表达式即为人们熟悉的数学运算式子写法。而前缀、后缀表达式是为了计算机计算方便的写法。
前缀表达式是一种没有括号的算术表达式,与中缀表达式不同的是,其将运算符写在前面,操作数写在后面。后缀表达式则是将操作数写在前面,运算符写在后面。
前缀表达式又称波兰表达式,后缀表达式又称逆波兰表达式。通过利用栈的特性,我们可以从中缀表达式得到前后缀表达式,轻松的计算出其结果。
我们先写一组表达式,后面就用这组表达式:
中缀:9*8+(14-8/2)*3
前缀:+ * 9 8 * - 14 / 8 2 3 //这里把每一个操作数或运算符用空格分开
后缀:9 8 * 14 8 2 / - 3 * +
如何存储前缀和后缀表达式呢?如果直接用字符串,那么数字之前就会混乱,所以我用了结构体存储:
struct A
{int a;char type; //数字为0,符号为1
};
struct Poland //前缀/后缀表达式的存储方式 不用string的原因是,无法从字符串中分开操作数
{char type; //p前缀 s后缀int lenth;A *content;
};
2.中缀表达式转前缀表达式
①初始化两个栈S1,S2,从右往左扫描中缀式。
②遇到操作数:将其压入S2栈。
③遇到括号时:
若是右括号‘)’,将其压入S1栈;
若是左括号‘(’,则依次弹出S1栈的元素,并将其压入S2栈,直至S1栈栈顶元素为右括号‘)’,弹出这个右括号。
④遇到运算符时:
若S1栈为空或者其栈顶元素为右括号‘)’,将其压入S1。
否则,比较运算符和S1栈顶运算符(一定是运算符)的优先级:
若其优先级≥S1栈顶运算符,则将其压入S1;
若小于,则将S1栈顶运算符弹出并压入S2,重新进行步骤④。
⑤重复②③④直到到达中缀式最左边,将S1剩余元素压入S2。最后依次取出S2元素即为前缀表达式。
代码:
/*已知中缀表达式,求前缀表达式*/
Poland infix_to_prefix(string infix)
{stack<A> s1, s2;regex p("(\\d+)"); //用于查找数字的正则式string s = infix;reverse(s.begin(), s.end()); //求前缀式需要从右往左扫描,为了方便,直接颠倒字符串string::const_iterator iter = s.begin();string::const_iterator iter_end = s.end();while (iter < iter_end){smatch result;if (iter >= iter_end) //防止迭代器位置超出字符串break;if (*iter >= '0'&&*iter <= '9') //遇操作数 这里不能直接用regex_search,必须要确定后面的字符串第一个字符是数字{regex_search(iter, iter_end, result, p);string temp = result[0];int num = atoi(temp.c_str()); //string转int c_str函数返回当前字符串的首字符地址A sym1;sym1.a = num;sym1.type = 0;s2.push(sym1);iter = result[0].second; //别忘了更新迭代器位置,这里second表示查找到的数字在源串中后面部分串的首地址}else //遇符号{char ope = *iter;A sym2;sym2.a = (int)ope;sym2.type = 1;label:if (ope == ')') //遇右括号,直接入栈{s1.push(sym2);}else if (ope == '(') //遇左括号,将栈内直到右括号之前的元素依次弹出压入S2{while (s1.top().a != (int)')'){A t = s1.top();s1.pop();s2.push(t);}//这里让)也出栈s1.pop();}else //其他运算符,如 %/* >= +-{if (s1.empty() || (s1.top().a == (int)')'&&s1.top().type == 1)) //若s1为空或其栈顶为),符号入栈s1 或者符号优先级>=栈顶符号s1.push(sym2);else{int pri = compare_priority(ope, (char)s1.top().a); //这里判断符号优先级 1大于 0相等 -1小于if (pri >= 0){s1.push(sym2);}else if (pri == -1) //若优先级小于栈顶符号,则将栈顶符号弹出压入S2,继续比较{s2.push(s1.top());s1.pop();goto label; //直接goto前面判断符号的开始位置}}}iter++; //别忘了更新迭代器位置}}while (!s1.empty()){s2.push(s1.top());s1.pop();}/*以下部分为存储前缀表达式*/Poland prefix;prefix.type = 'p';prefix.lenth = s2.size();prefix.content = new A[prefix.lenth]; //结构体中定义的是存储首地址,需要在这里开辟合适大小的数组空间,存储length个操作数或符号数据int k = 0;while (!s2.empty()){A res = s2.top();A nres;s2.pop();if (res.type == 0){string numm = to_string(res.a);reverse(numm.begin(), numm.end()); //因为之前将中缀式颠倒时,数字也颠倒了。这里要改回来nres.type = 0;nres.a = atoi(numm.c_str());}else{nres.type = 1;nres.a = res.a;}*(prefix.content + k) = nres;k++;}return prefix;}
3.求前缀表达式的值
①初始化一个栈S,从右往左扫描前缀式。
②遇到操作数时,则将其压入栈S。
③遇到运算符时,取出栈S的栈顶元素和次顶元素,运行运算。栈顶元素 运算符 次顶元素 = 结果。将结果压入栈S。
④重复②③直到到达前缀式最左边,此时栈S栈顶元素即为计算结果。
代码:
/*求前缀表达式的值*/
int cal_prefix(Poland prefix)
{stack<A> s;int i = prefix.lenth - 1;while (i >= 0){A temp = *(prefix.content + i);if (temp.type == 0){s.push(temp);}else{A num1 = s.top();s.pop();A num2 = s.top();s.pop();A res;res.type = 0;if (temp.a == (int)'+'){res.a = num1.a + num2.a;}else if (temp.a == (int)'-'){res.a = num1.a - num2.a;}else if (temp.a == (int)'*'){res.a = num1.a * num2.a;}else if (temp.a == (int)'/'){res.a = num1.a / num2.a;}else if (temp.a == (int)'%'){res.a = num1.a % num2.a;}s.push(res);}i--;}return s.top().a;
}
4.中缀表达式转后缀表达式
与2类似,最主要的区别是,这里采用正序扫描。且左右括号的情况反过来了,且优先级比较处有微小区别,且最后结果要倒过来。
①初始化两个栈S1,S2,从右往左扫描中缀式。
②遇到操作数:将其压入S2栈。
③遇到括号时:
若是左括号‘(’,将其压入S1栈;
若是右括号‘)’,则依次弹出S1栈的元素,并将其压入S2栈,直至S1栈栈顶元素为左括号‘(’,弹出这个左括号。
④遇到运算符时:
若S1栈为空或者其栈顶元素为左括号‘(’,将其压入S1。
否则,比较运算符和S1栈顶运算符(一定是运算符)的优先级:
若其优先级>S1栈顶运算符,则将其压入S1;
若其优先级≤S1栈顶运算符,则将S1栈顶运算符弹出并压入S2,重新进行步骤④。
⑤重复②③④直到到达中缀式最右边。将S1剩余元素压入S2。最后依次取出S2元素进行倒序即为后缀表达式。
代码:
Poland infix_to_suffix(string infix)
{stack<A> s1, s2;regex p("(\\d+)"); //用于查找数字的正则式string s = infix;string::const_iterator iter = s.begin();string::const_iterator iter_end = s.end();while (iter < iter_end){smatch result;if (iter >= iter_end) //防止迭代器位置超出字符串break;if (*iter >= '0'&&*iter <= '9') //遇操作数 这里不能直接用regex_search,必须要确定后面的字符串第一个字符是数字{regex_search(iter, iter_end, result, p);string temp = result[0];int num = atoi(temp.c_str()); //string转int c_str函数返回当前字符串的首字符地址A sym1;sym1.a = num;sym1.type = 0;s2.push(sym1);iter = result[0].second; //别忘了更新迭代器位置,这里second表示查找到的数字在源串中后面部分串的首地址}else //遇符号{char ope = *iter;A sym2;sym2.a = (int)ope;sym2.type = 1;label:if (ope == '(') //遇左括号,直接入栈{s1.push(sym2);}else if (ope == ')') //遇右括号,将栈内直到左括号之前的元素依次弹出压入S2{while (s1.top().a != (int)'('){A t = s1.top();s1.pop();s2.push(t);}//这里让(也出栈s1.pop();}else //其他运算符,如 %/* >= +-{if (s1.empty() || (s1.top().a == (int)'('&&s1.top().type == 1)) //若s1为空或其栈顶为(,符号入栈s1 或者符号优先级>栈顶符号s1.push(sym2);else{int pri = compare_priority(ope, (char)s1.top().a); //这里判断符号优先级 1大于 0相等 -1小于if (pri > 0){s1.push(sym2);}else if (pri == -1||pri==0) //若优先级小于等于栈顶符号,则将栈顶符号弹出压入S2,继续比较{s2.push(s1.top());s1.pop();goto label; //直接goto前面判断符号的开始位置}}}iter++; //别忘了更新迭代器位置}}while (!s1.empty()){s2.push(s1.top());s1.pop();}/*以下部分为存储后表达式*/Poland suffix;suffix.type = 's';suffix.lenth = s2.size();suffix.content = new A[suffix.lenth]; //结构体中定义的是存储首地址,需要在这里开辟合适大小的数组空间,存储length个操作数或符号数据int k = suffix.lenth-1; //这里要倒过来while (!s2.empty()){A res = s2.top();A nres;s2.pop();if (res.type == 0){string numm = to_string(res.a);nres.type = 0;nres.a = atoi(numm.c_str());}else{nres.type = 1;nres.a = res.a;}*(suffix.content + k) = nres;k--;}return suffix;
}
5.求后缀表达式的值
与3类似,同样这里要正序扫描,且次顶元素在运算符前。
①初始化一个栈S,从右往左扫描后缀式。
②遇到操作数时,则将其压入栈S。
③遇到运算符时,取出栈S的栈顶元素和次顶元素,运行运算。次顶元素 运算符 栈顶元素 = 结果。将结果压入栈S。
④重复②③直到到达前缀式最右边,此时栈S栈顶元素即为计算结果。
代码:
/*求后缀表达式的值*/
int cal_suffix(Poland suffix)
{stack<A> s;int i = 0;while (i < suffix.lenth){A temp = *(suffix.content + i);if (temp.type == 0){s.push(temp);}else{A num2 = s.top();s.pop();A num1 = s.top();s.pop();A res;res.type = 0;if (temp.a == (int)'+'){res.a = num1.a + num2.a;}else if (temp.a == (int)'-'){res.a = num1.a - num2.a;}else if (temp.a == (int)'*'){res.a = num1.a * num2.a;}else if (temp.a == (int)'/'){res.a = num1.a / num2.a;}else if (temp.a == (int)'%'){res.a = num1.a % num2.a;}s.push(res);}i++;}return s.top().a;
}
6.测试
int main() // 9*8+(14-8/2)*3 =102 14-31%(19-8*2)+(56-33/(12-4)) =65
{string infix;cin >> infix;Poland prefix = infix_to_prefix(infix);Poland suffix = infix_to_suffix(infix);cout << endl;cout << "前缀表达式的长度为:" << prefix.lenth << endl;for (auto i = 0; i < prefix.lenth; i++){A temp = *(prefix.content + i);string ts = "";if (temp.type == 0)ts += to_string(temp.a);elsets += (char)temp.a;cout << ts << " ";}cout << endl;cout <<"前缀表达式的计算值为:"<< cal_prefix(prefix) << endl; cout << endl;cout << "后缀表达式的长度为:" << suffix.lenth << endl;for (auto i = 0; i < suffix.lenth; i++){A temp = *(suffix.content + i);string ts = "";if (temp.type == 0)ts += to_string(temp.a);elsets += (char)temp.a;cout << ts << " ";}cout << endl;cout << "后缀表达式的计算值为:" << cal_suffix(suffix) << endl;return 0;
}
分别尝试了三组中缀表达式,结果如下:
9*8+(14-8/2)*3 =102
14-31%(19-8*2)+(56-33/(12-4)) =65
3+(7245/167+54*(6522-681%13+192))*7 =2536306
前缀表达式与后缀表达式求法(栈的应用)相关推荐
- 数据结构 - 栈 (逆波兰计算器)(栈的三种表达式)(前缀、中缀和后缀表达式,后缀也叫逆波兰表达式)(中缀表达式转后缀表达式实现步骤及完整代码)
栈的三种表达式:前缀.中缀和后缀表达式,后缀也叫逆波兰表达式 前缀(波兰表达式) 中缀(对人来讲很好理解,对于计算机来讲就方便了,一般会把中缀表达式转换成后缀表达式) 后缀(逆波兰表达式) 计算过程 ...
- 前缀、中缀和后缀表达式详解,中缀表达式到后缀表达式的转换规则,以及后缀表达式的计算规则,附计算代码
1. 中缀.前缀和后缀表达式 1.1 中缀表达式 首先,中缀表达式的这个"缀"指运算符在两个操作数的位置.中缀表达式其实就是我们常用的算术表达式,比如 2 + 9 - (32 * ...
- 中缀表达式到前缀表达式和后缀表达式
1.算法思路 转化为后缀:从左到右遍历中缀表达式,遇到操作数,输出,遇到操作符,当前操作符的优先级大于栈顶操作符优先级,进栈,否则,弹出栈顶优先级大于等于当前操作符的操作符,当前操作符进栈. ...
- 中缀表达式转换成前缀表达式和后缀表达式的极其简单方法
35,15,+,80,70,-,*,20,/ //后缀表达方式 (((35+15)*(80-70))/20)=25 //中缀表达方式 /,*,+,35,15,-,80,70, 20 //前缀表达方式 ...
- 前缀表达式、中缀表达式、后缀表达式的区别
一.三者的概念(参考维基百科) 1.1中缀表达式 中缀表达式是符合人类直觉的一种表达方式,其特点是操作符(二元操作符)在中间,操作数在两侧. 例如 3 + 4 , 5 - 6 * 7, ( ...
- 前缀表达式与后缀表达式
前缀表达式与后缀表达式都可以由中缀表达式来转换而成,由于在转化的过程中已经考虑了优先级,所以前缀表达式和后缀表达式的求值直接借助栈就可以,不再有优先级的规则. 中缀表达式转换为前缀表达式和后缀表达式都 ...
- 前缀表达式,中缀表达式和后缀表达式的定义与联系(超详细)
目录 前缀.中缀.后缀表达式 前缀表达式 前缀表达式的计算机求值 中缀表达式 后缀表达式 后缀表达式的计算机求值 中缀表达式转化为前缀和后缀表达式 小结 前缀.中缀.后缀表达式 前缀.中缀.后缀表达式 ...
- 【数据结构与算法】【12】前缀表达式、中缀表达式、后缀表达式
什么是前缀表达式.中缀表达式.后缀表达式 前缀表达式.中缀表达式.后缀表达式,是通过树来存储和计算表达式的三种不同方式 以如下公式为例 (a+(b−c))∗d( a+(b-c) )*d(a+(b−c) ...
- 前缀表达式和后缀表达式 - C++代码
目录 速览 前缀表达式 前缀表达式的运算规则 中缀表达式转换为前缀表达式 后缀表达式 后缀表达式的运算 中缀表达式转换为后缀表达式 刷题向文章,不介绍原理,只介绍规则 速览 算术表达式分为: 前缀表达 ...
最新文章
- android studio 模拟器中文乱码
- django项目连接远程数据库
- mysql 不在另一个表中_MySQL选择查询从表中选择不在另一个表中的行?
- JAVA正确地自定义比较对象---如何重写equals方法和hashCode方法
- SMTP协议初探(二)----linux下c编程实现发邮件
- Android java获取行号和函数名
- 序列最小最优化算法(SMO) SVM凸优化求
- 汇编语言王爽老师第四版(寄存器解读与实验二)
- 采用加速度传感器的角度测量仪系统硬件电路设计_温度传感器电路
- 单元测试、集成测试、功能测试——Unittest
- 解决问题最重要的习惯不是一直盯着屏幕和编写修改代码,某些时候,阻止你成功的东西恰恰会是过于努力。这时候你需要暂停一下,平缓你的思绪,换一种方法或许能带给你不一样的效果。
- 苹果开放降级通道_今天下午 iOS 降级通道打开?骗子!
- pygame-KidsCanCode系列jumpy-part0-使用sprite
- php汉字转拼音 php 汉字取首字母
- 公司要我做报表,只会随机数据图表的我感到危机感,来试试Python读取csv
- CSDN高校俱乐部2012年秋季巡讲安排及讲师介绍
- php ctr b,用PHP解密AES CTR Little Endian
- admin_move_table的重组机制验证(失败了)
- HTML5 Canvas 北京的美丽园林和雾霾天气
- 使用ModbusPoll与Modbus Slave进行TCP连接和串口连接
热门文章
- 录播系统的服务器有哪些,录播系统,录播教室,录播服务器的十大特色_航天广电...
- 基于B_S校医院管理系统的设计与实现
- 实现一个联系客服对话框的前端部分
- 【NB-Iot自我学习之路_3】NB平台介绍【电信篇】+【移动篇】
- C#超级通信调试工具
- 从零玩转Node.js,助你打通前后端任督二脉
- 矩阵的负二分之一怎么计算
- 网上特工网络监控系统 v5.65 官网
- 充分条件,必要条件,充分必要条件
- STP的概念和基本内容