中缀表达式到前缀表达式的快速算法
- 格式:pdf
- 大小:2.19 MB
- 文档页数:4
字符串的四则运算四则运算,最常⽤的当然是逆波兰⽅法,现将表达式由中缀表达式转化为后缀表达式,然后再使⽤栈计算即可。
这两步下来,估计没有三四百⾏代码是实现不了的。
中缀表达式转前缀后缀表达式将中缀表达式转换为后缀表达式的算法思想:数字时,加⼊后缀表达式;运算符:a. 若为 '(',⼊栈;b. 若为 ')',则依次把栈中的的运算符加⼊后缀表达式中,直到出现'(',从栈中删除'(' ;c. 若为除括号外的其他运算符,当其优先级⾼于除'('以外的栈顶运算符时,直接⼊栈。
否则从栈顶开始,依次弹出⽐当前处理的运算符优先级⾼和优先级相等的运算符,直到⼀个⽐它优先级低的或者遇到了⼀个左括号为⽌。
⾼优先级可以压迫低优先级!⼈⼯实现转换这⾥我给出⼀个中缀表达式:a+b*c-(d+e)第⼀步:按照运算符的优先级对所有的运算单位加括号:式⼦变成了:((a+(b*c))-(d+e))第⼆步:转换前缀与后缀表达式前缀:把运算符号移动到对应的括号前⾯,则变成了:-( +(a *(bc)) +(de)) ,把括号去掉:-+a*bc+de 前缀式⼦出现。
后缀:把运算符号移动到对应的括号后⾯,则变成了:((a(bc)* )+ (de)+ )- ,把括号去掉:abc*+de+- 后缀式⼦出现。
⽐如:计算(2 + 1) * (13 + 5)转换后得:((2+1)*(13+5)) -> ((2 1) + (13 5) +) * -> 2 1 + 13 5 + *这⾥把后缀表达式存储到vector<string>中,实现栈的计算,如下:int cal(int num1, int num2, string tag){if ("+" == tag){return num1 + num2;}else if ("-" == tag){return num1 - num2;}else if ("*" == tag){return num1*num2;}else{return num1 / num2;}}int evalRPN(vector<string> &tokens) {int result = 0;stack<int> nums;for (int i = 0; i<tokens.size(); i++){string tag = tokens[i];if (tag != "+"&&tag != "-"&&tag != "*"&&tag != "/"){//这是⼀个数字nums.push(atoi(tag.c_str()));}else{//不是⼀个数字int num2 = nums.top();nums.pop();int num1 = nums.top();nums.pop();result = cal(num1, num2, tag);nums.push(result);}}return result=nums.top();}实际中遇到的笔试题是这样的,有字符串表⽰的⼀个四则运算表达式,要求计算出该表达式的正确数值,⽐如:5+17*8-4/2。
关于中缀表达式求值(四则运算和括号,⽀持多位数)1.中缀转后缀和计算后缀表达式 1.1 中转后1. 数字则输出2. 如果是 "(" ,直接⼊栈3. 如果是 ")", 栈顶元素出栈并输出,直到遇到" (",只弹出。
4. 如果栈空,直接⼊栈,否则和栈顶⽐较优先级,当⼤于栈顶时,⼊栈,否则,栈顶元素退栈,直到⼤于栈顶或栈空5. 中缀表达式扫描完毕,若栈不空,将其内所有元素弹出。
1.2 计算后缀1. 数字则⼊栈2. 运算符,出栈b,再出栈a 计算c=a operate b,将c⼊栈3. 中缀表达式扫描完毕,将栈内唯⼀的元素(也就是求的值)弹出并返回。
2.代码如下import java.util.*;import java.util.regex.Matcher;import java.util.regex.Pattern;;public class Test4 {private static Map<Character,Integer> map=new HashMap<>();static {map.put('+', 0);map.put('-', 0);map.put('*', 1);map.put('/', 1);}private static List<String> toPost(String target){List<String> post=new ArrayList<>();Stack<Character> stk=new Stack<>();Pattern p=pile("\\d+|\\D");Matcher m=p.matcher(target);while(m.find()) {String e=m.group();if(e.matches("\\d+"))post.add(e);else if(e.equals("("))stk.push('(');else if(e.equals(")")){while(stk.peek()!='(')post.add(stk.pop()+"");stk.pop();}else {char op=e.charAt(0);while(!stk.isEmpty()&&stk.peek()!='('&&map.get(op)<=map.get(stk.peek()))post.add(stk.pop()+"");stk.push(op);}}while(!stk.isEmpty()) post.add(stk.pop()+"");return post;}private static int calcuPost(List<String> post) {Stack<Integer> stk=new Stack<>();for(String s:post) {if(s.matches("\\d+")) stk.push(Integer.parseInt(s));else {char c=s.charAt(0);int b=stk.pop();int a=stk.pop();int t=0;if(c=='+')t=a+b;else if(c=='-')t=a-b;else if(c=='*')t=a*b;elset=a/b;stk.push(t);}}return stk.pop();}public static void main(String[] args) {Scanner is=new Scanner(System.in);while(is.hasNext()) {String exp=is.next();System.out.println(calcuPost(toPost(exp))); }is.close();}}。
“中序表达式”转换为“前序表达式”、“后序表达式” 上周末参照书本写了个“计算器”的程序,其中最令我费解的就是“前序表达式”、“后续表达式”,好像记得⽼师在上课的时候讲过,估计当时也没听懂,看的稀⾥糊涂的,不过现在⼤概明⽩了…… 在此仅做以笔记。
⾸先看下⾯所⽰表格:中序表达式2*3/(2-1)+3*(4-1)前序表达式+/*23-21*3-41后序表达式23*21-/341-*+ 中序表达式对我们⽽⾔是很直观的(我们平时接触的就是这个),但计算机处理起来⽐较⿇烦(括号、优先级之类的),前序和后序表达式中没有括号,⽽且在计算中只需单向扫描,不需要考虑运算符的优先级。
以前序表达式“+/*23-21*3-41”为例,从右往左,先取出两个操作数“1”、“4”和⼀个运算符“-”,计算“4-1”,将结果3回填到字符串中,现在字符串变为“+/*23-21*33”。
再从右⾄左取两个数“3”、“3”和“*”,计算“3*3”,将结果“9”回填到字符串,得“+/*23-219’”, 再取数,连续取出“9”、“1”、“2”,直到取出⼀个运算符“-”,将与运算符最近的两个操作数进⾏计算,即“2-1”得“1”,回填字符串中,现在为“+/*239” 重复上述步骤,取出“2*3”=6,回填字符串得到“+/619”, 再取“6/1”=6,得到“+69”, 再取“6+9”=15。
运算完毕。
即从右⾄左取数,直到取出⼀个运算符,将刚取出的紧挨着运算符的两个操作数按运算符进⾏计算,结果回填⾄运算符。
重复该步骤,直到最后只剩下⼀个字符串则剩下的字符串即为结果。
后序表达式的字符串扫描⽅式正好和前序相反,是从左往右扫描,规则类似。
中序表达式转前序表达式步骤1、反转输⼊字符串,如“2*3/(2-1)+3*(4-1)” 反转后为“ )1-4(*3+)1-2(/3*2”,2、从字符串中取出下⼀个字符 2.1.如果是操作数,则直接输出 2.2.如果是“)”,压⼊栈中 2.3.如果是运算符但不是“(”,“)”,则不断循环进⾏以下处理 2.3.1.如果栈为空,则此运算符进栈,结束此步骤 2.3.2.如果栈顶是“)”,则此运算符进栈,结束此步骤 2.3.2.如果此运算符与栈顶优先级相同或者更⾼,此运算符进栈,结束此步骤 2.3.4.否则,运算符连续出栈,直到满⾜上述三个条件之⼀,然后此运算符进栈 2.4、如果是“(”,则运算符连续出栈,直到遇见“)”为⽌,将“)”出栈且丢弃之3、如果还有更多的字符串,则转到第2步4、不在有未处理的字符串了,输出栈中剩余元素5、再次反转字符串得到最终结果 我第⼀次看到这个的时候就没看懂是什么意思,在⽹上查了点,⼜瞪了它好久才明⽩了,就以“2*3/(2-1)+3*(4-1),”为例做以下说明: 2*3/(2-1)+3*(4-1),反转得“ )1-4(*3+)1-2(/3*2 ”; 取第⼀个字符串为“)”,⼊栈(此时栈中为“)”); 取下⼀个“1”,是操作数,直接输出(⽬前输出“1”); 取下⼀个“-”,既不是“)”,也不是“(”,则转到2.3,此时栈顶为“)”,则该运算符进栈(栈中为“-、)”); 取下⼀个“4”,直接输出(⽬前输出的是“14”); 取下⼀个“(”,运算符连续出栈(栈中此时为“-、)”),直到遇见“)”,此时输出“-”(⽬前输出“14-”,栈为空); 取下⼀个“*”,既不是“)”,也不是“(”,则转到2.3,进栈(栈为空); 取下⼀个“3”,直接输出(⽬前输出“14-3”); 取下⼀个“+”,此时栈顶为“*”,“+”的优先级⽐“*”低(2.3.4),则运算符连续出栈(只有⼀个*出栈,此时栈为空符合2.3.1,继续下⼀步),“+”进栈; 取下⼀个“)”,进栈(此时栈中为“)、+”); 取下⼀个“1”直接输出(⽬前输出为14-3*1); 取下⼀个“-”,此时栈顶为“)”,“-”进栈(栈中此时为“-、)、+”); 取下⼀个“2”,直接输出(⽬前输出“14-3*12”); 取下⼀个“(”,运算符连续出栈,直到遇见“)”,此时栈中为“-、)、+”,输出-,且抛弃“)”,此时输出为“14-3*12-”,栈中为“+”; 取下⼀个“/”,优先级⽐栈顶“+”⾼,此运算符进栈; 取下⼀个“3”,直接输出(此时输出“14-3*12-3”); 取下⼀个“*”,优先级⽐栈顶“+”⾼,此运算符进栈; 取下⼀个“2”,输出(此时输出“14-3*12-32”); 不在有未处理的运算符,输出栈中剩余元素,结果的“14-3*12-32*/+”; 反转字符串的“+/*23-21*3-41”。
前缀中缀后缀相互转换符号说明•为了表示简便,程序中符号如下•¬, 非•∨,或•∧, 与•→, 推出•=, 等价中缀转后缀•例:P∨Q∧R∨(T=S)输出:PQR∧∨TS=∨算法流程:1.初始化两个栈:运算符栈S1,储存中间结果的栈S2;2.从左至右扫描中缀表达式:1.遇到操作数2.遇到运算符3.遇到括号P∨Q∧R∨(T=S)中缀转后缀算法流程:1.初始化两个栈:运算符栈S1,储存中间结果的栈S2;2.从左至右扫描中缀表达式:1.遇到操作数:直接压入S22.遇到运算符3.遇到括号P∨Q∧R∨(T=S)P中缀转后缀算法流程:1.初始化两个栈:运算符栈S1,储存中间结果的栈S2;2.从左至右扫描中缀表达式:1.遇到操作数2.遇到运算符:1.如果S1为空,或S1栈顶为左括号“(”,该运算符压入S1;P∨Q∧R∨(T=S)2.若优先级高于栈顶运算符,该运算符压入S1;3.否则,S1栈顶运算符弹出并压入S2,重新进行2-2操作。
3.遇到括号∨P中缀转后缀算法流程:1.初始化两个栈:运算符栈S1,储存中间结果的栈S2;2.从左至右扫描中缀表达式:1.遇到操作数:直接压入S22.遇到运算符3.遇到括号P∨Q∧R∨(T=S)QP∨中缀转后缀算法流程:1.初始化两个栈:运算符栈S 1,储存中间结果的栈S 2;2.从左至右扫描中缀表达式:1.遇到操作数2.遇到运算符:1.如果S 1为空,或S 1栈顶为左括号“(”,该运算符压入S 1;2.若优先级高于栈顶运算符,该运算符压入S 1;3.否则,S 1栈顶运算符弹出并压入S 2,重新进行2-2操作。
3.遇到括号P ∨Q ∧R ∨(T =S)P ∨Q ∧中缀转后缀算法流程:1.初始化两个栈:运算符栈S 1,储存中间结果的栈S 2;2.从左至右扫描中缀表达式:1.遇到操作数:直接压入S 22.遇到运算符:3.遇到括号P ∨Q ∧R ∨(T =S)P ∨Q ∧R中缀转后缀算法流程:1.初始化两个栈:运算符栈S 1,储存中间结果的栈S 2;2.从左至右扫描中缀表达式:1.遇到操作数2.遇到运算符:1.如果S 1为空,或S 1栈顶为左括号“(”,该运算符压入S 1;2.若优先级高于栈顶运算符,该运算符压入S 1;3.否则,S 1栈顶运算符弹出并压入S 2,重新进行2-2操作。
前中后缀表达式的计算方法前缀表达式的计算方法:从右往左扫描表达式,遇到数字则入栈,遇到运算符则取出栈顶的两个数字进行运算,将结果入栈,最后栈中剩下的数字即是表达式的结果。
例如,前缀表达式*+567的计算过程如下:1.从右往左扫描表达式。
2.遇到数字7,入栈,栈:7。
3.遇到数字6,入栈,栈:67。
4.遇到数字5,入栈,栈:567。
5.遇到运算符+,取出栈顶的两个数字6和7进行加法运算,将结果13入栈,栈:513。
6.遇到运算符*,取出栈顶的两个数字5和13进行乘法运算,将结果65入栈,栈:65。
7.最后栈中剩下的数字65即是表达式的结果。
中缀表达式的计算方法:将中缀表达式转换为后缀表达式后,再用后缀表达式的计算方法进行计算。
例如,中缀表达式1+2*3的计算过程如下:1.将中缀表达式转换为后缀表达式123*+。
2.从左往右扫描后缀表达式。
3.遇到数字1,入栈,栈:1。
4.遇到数字2,入栈,栈:12。
5.遇到数字3,入栈,栈:123。
6.遇到运算符*,取出栈顶的两个数字2和3进行乘法运算,将结果6入栈,栈:16。
7.遇到运算符+,取出栈顶的两个数字1和6进行加法运算,将结果7入栈,栈:7。
8.最后栈中剩下的数字7即是表达式的结果。
后缀表达式的计算方法:从左往右扫描表达式,遇到数字则入栈,遇到运算符则取出栈顶的两个数字进行运算,并将结果入栈,最后栈中剩下的数字即是表达式的结果。
例如,后缀表达式56+7*的计算过程如下:1.从左往右扫描后缀表达式。
2.遇到数字5,入栈,栈:5。
3.遇到数字6,入栈,栈:56。
4.遇到运算符+,取出栈顶的两个数字5和6进行加法运算,将结果11入栈,栈:11。
5.遇到数字7,入栈,栈:117。
6.遇到运算符*,取出栈顶的两个数字11和7进行乘法运算,将结果77入栈,栈:77。
7.最后栈中剩下的数字77即是表达式的结果。
探讨由中缀求前、后缀表达式浙江慈溪实验中学 张利波 315300【联赛真题】试将下面的表达式改写成前缀与后缀的表示形式: (a) A+B*C/D (来源于第三届高中组初赛试题)相关知识:为了便于处理表达式,常常将普通表达式(称为中缀表示)转换为前缀{运算符在前,如X/Y 写为/XY}和后缀{运算符在后,如X/Y 写为XY/}的表达式。
错误解析:按传统解题思路,由遍历特点画出相应二叉树(如图1所示),由二叉树得到前序遍历:+A*B/CD ,后序遍历:ABCD/*+。
【解析】 乍一看,还以为正确的,错误的原因是更改了运算顺序,表达式中*/属于同一级别,按照从左到右的顺序依次进行,构造的树先执行/,再执行*,原因在于此。
由此可见,运算优先顺序是反映在树的深度上,越深说明运算优先级别越高,越早参与运算。
故构造二叉树时,可以从优先级别越高的表达式,按照从树的深度开始逐级向根方向构造。
正确的二叉树如图2所示的二叉树,由该二叉树获得的前序遍历:+A/*BCD ,后序遍历:ABC*D/+,此为正确答案。
【简单解法】对于初学者,二叉树遍历是有难度的,用二叉树先构造后遍历,有点舍近求远?其实,单单用运算先后顺序的知识就可快速生成前、后缀表达式。
如中缀表达式A/B ,前缀表达式就把符号写在两个操作数的前面,即/AB ,值得提醒的是:这里的AB 可分别扩展成表达式,即嵌套多步运算,具体解法是按运算级别由里及外,依次转换。
表1为例题A+B*C/D 前缀表达式生成的过程。
最后只要把表1转换后的前缀表达式:+A(/(*BC)D),去掉括号即可:+A/*BCD 。
前后缀表示的优点:可以不用括号即可确定求值的顺序。
○+ ○A○* ○B ○/○C○D图1○+ ○A ○/ ○* ○D ○B○C图2。
中缀表达式转换为前缀表达式中缀表达式转换为前缀表达式在《》中,我们讨论了对前缀表达式如何计算:设置⼀个操作数栈,对前缀表达式从右到左扫描,遇到操作数直接⼊栈,遇到操作符则从操作数栈弹栈,先弹left值后弹right值,根据操作符进⾏相应的计算,并将计算结果压⼊到操作数栈中,最终将整个前缀表达式扫⾯完毕。
这时操作数栈中只有⼀个元素,该元素的值即为前缀表达式的值。
在《》中,我们讨论了如何将⼀个中缀表达式转换为其对应的后缀表达式。
其思想为:设置⼀个操作符栈,如果遇到操作数,则直接将操作数放进后缀表达式中,如果遇到⾮操作数,则:如果是左括号,则将左括号⼊栈;如果是右括号,则从操作符栈中将操作符弹栈,放⼊后缀表达式中,直⾄栈空或遇到栈中的左括号,并将左括号弹栈;如果是其他操作符,则⽐较其优先级与栈中操作符优先级情况,如果栈中的操作符的优先级⼤于等于当前操作符,则将栈中操作符弹栈,直⾄栈空,或栈中操作符优先级⼩于当前操作符的优先级,将当前操作符压栈。
当从左到右顺序扫描完整个中缀表达式后,检测操作符栈,如果⾮空,则依次弹栈,将弹出的操作符依次压⼊到后缀表达式中。
最终,得到中缀表达式对应的后缀表达式。
如果还想计算后缀表达式的值,则可以参考《》。
本⽂我们是讨论如何将中缀表达式转换为前缀表达式。
我们先给出中缀表达式转换前缀表达式的程序,然后再对程序进⾏相关讲解,之后在与中缀表达式转换后缀表达式的过程进⾏⽐较,分析其中的差异存在于哪⾥。
// 中缀表达式转换为前缀表达式#include <iostream>#include <vector>#include <string>#include <sstream>#include <map>#include <stack>#include <algorithm>using namespace std;void GetInfix(vector<string>& infix){infix.clear();string line;getline(cin, line);istringstream sin(line);string tmp;while (sin >> tmp){infix.push_back(tmp);}}// 初始化操作符void InitOperators(map<string, int>& opers){opers.clear();opers["("] = 100;opers[")"] = 900;opers["+"] = 100;opers["-"] = 100;opers["*"] = 200;opers["/"] = 200;}bool IsOperator(const string& op, const map<string, int>& opers){auto cit = opers.find(op);if (cit != opers.end()){return true;}else{return false;}}void InfixToPrefix(const vector<string>& infix, vector<string>& prefix, map<string, int>& opers){prefix.clear();stack<string> stk; // 操作符栈for (int i = infix.size() - 1; i >= 0; --i) // 从右到左扫描{if (!IsOperator(infix[i], opers)) // 如果是操作数{prefix.push_back(infix[i]);}else// 如果是操作符{if (infix[i] == ")") // 如果是右括号,则直接⼊栈{stk.push(infix[i]);}else if (infix[i] == "(") // 如果是左括号{// 依次弹出栈中的操作符,直⾄遇到右括号while (!stk.empty()){if (stk.top() == ")"){stk.pop();break;}else{prefix.push_back(stk.top());stk.pop();}}}else// 如果是其他操作符{while (!stk.empty() && stk.top() != ")" && opers[stk.top()] > opers[infix[i]]) // 栈顶操作符优先级⼤于当前操作符优先级{prefix.push_back(stk.top());stk.pop();}// 将当前操作符⼊栈stk.push(infix[i]);}}}// 检测操作符栈是否为空while (!stk.empty()){prefix.push_back(stk.top());stk.pop();}// 将prefix翻转reverse(prefix.begin(), prefix.end());return;}void Display(const vector<string>& fix){for (auto i = 0; i != fix.size(); ++i){cout << fix[i] << '';}cout << endl;}int main(){map<string, int> opers;InitOperators(opers);while (true){vector<string> infix, prefix;GetInfix(infix);Display(infix);InfixToPrefix(infix, prefix, opers);Display(prefix);cout << endl;}return0;}⾸先说明的是,我们的中缀表达式输⼊是⽤空⽩符间隔的,⽽没有对中缀表达式进⾏词法分析,对中缀表达式的词法分析可以参考《》。
C#算术表达式求值(后缀法),看这⼀篇就够了⼀、种类介绍算术表达式有三种:前缀表达式、中缀表达式和后缀表达式。
⼀般⽤的是中缀,⽐如1+1,前后缀就是把操作符移到前⾯和后⾯,下⾯简单介绍⼀下这三种表达式。
1、前缀表⽰法前缀表⽰法⼜叫波兰表⽰法,他的操作符置于操作数的前⾯(例:+ 1 2),是波兰数学家扬·武卡谢维奇1920年代引⼊的,⽤于简化命题逻辑。
因为我们⼀般认为操作符是在操作数中间的,所以在⽇常⽣活中⽤的不多,但在计算机科学领域占有⼀席之地。
⼀般的表⽰法对计算机来说处理很⿇烦,每个符号都要考虑优先级,还有括号这种会打乱优先级的存在,将使计算机花费⼤量的资源进⾏解析。
⽽前缀表⽰法没有优先级的概念,他是按顺序处理的。
举个例⼦:9-2*3这个式⼦,计算机需要先分析优先级,先乘后减,找到2*3,再进⾏减操作;化成前缀表⽰法就是:- 9 * 2 3,计算机可以依次读取,操作符作⽤于后⼀个操作数,遇到减就是让9减去后⾯的数,⽽跟着9的是乘,也就是说让9减去乘的结果,这对计算机来说很简单,按顺序来就⾏了。
2、中缀表⽰法这也就是我们⼀般的表⽰法,他的操作符置于操作数的中间(例:1 + 2),前⾯也说过这种⽅法不容易被计算机解析,但他符合⼈们的普遍⽤法,许多编程语⾔也就⽤这种⽅法了。
在中缀表⽰法中括号是必须有的,要不然运算顺序会乱掉。
3、后缀表⽰法后缀表⽰法⼜叫逆波兰表⽰法,他的操作符置于操作数的后⾯(例:1 2 +),他和前缀表⽰法都对计算机⽐较友好,但他很容易⽤堆栈解析,所以在计算机中⽤的很多。
他的解释过程⼀般是:操作数⼊栈;遇到操作符时,操作数出栈,求值,将结果⼊栈;当⼀遍后,栈顶就是表达式的值。
因此逆波兰表达式的求值使⽤堆栈结构很容易实现,且能很快求值。
注意:逆波兰记法并不是简单的波兰表达式的反转。
因为对于不满⾜交换律的操作符,它的操作数写法仍然是常规顺序,如,波兰记法/ 6 3的逆波兰记法是6 3 /⽽不是3 6 /;数字的数位写法也是常规顺序。
前缀中缀后缀表达式转换题目摘要:一、前缀表达式的概念和作用1.前缀表达式的定义2.求解前缀表达式的算法二、中缀表达式的概念和作用1.中缀表达式的定义2.将前缀表达式转换为中缀表达式的步骤三、后缀表达式的概念和作用1.后缀表达式的定义2.将中缀表达式转换为后缀表达式的步骤四、前缀中缀后缀表达式转换的应用1.算术表达式的计算2.复杂数字运算的简化正文:一、前缀表达式的概念和作用前缀表达式是一种用于计算算术表达式的数据结构,通过将运算符和操作数以特定顺序存储,以便在进行计算时能够高效地访问和处理。
前缀表达式的定义包括运算符和操作数两部分,它们按照从左到右的顺序排列。
求解前缀表达式的算法通常采用递归或栈来实现。
二、中缀表达式的概念和作用中缀表达式是与前缀表达式相对应的一种数据结构,其中运算符和操作数按照从左到右的顺序排列,但在操作数之间插入运算符。
将前缀表达式转换为中缀表达式有助于更直观地理解算术表达式的计算过程。
具体的转换步骤包括:1.初始化一个空字符串表示中缀表达式2.遍历前缀表达式,将操作数添加到中缀表达式字符串中3.遍历前缀表达式,将运算符添加到中缀表达式字符串中,并在需要的地方插入空格以区分操作数和运算符三、后缀表达式的概念和作用后缀表达式是与前缀表达式和中缀表达式相对应的一种数据结构,其中运算符和操作数按照从右到左的顺序排列。
将中缀表达式转换为后缀表达式有助于实现高效的计算算法。
具体的转换步骤包括:1.初始化一个空字符串表示后缀表达式2.遍历中缀表达式,将操作数添加到后缀表达式字符串中3.遍历中缀表达式,将运算符添加到后缀表达式字符串中,并在需要的地方插入空格以区分操作数和运算符4.对后缀表达式进行排序,以便在计算过程中能够高效地访问和处理四、前缀中缀后缀表达式转换的应用前缀、中缀和后缀表达式转换在计算算术表达式和简化复杂数字运算方面具有广泛的应用。
通过对表达式进行转换,可以实现高效的数据结构和算法设计,从而提高计算性能。
C语言实现中缀、后缀、前缀表达式相互转化并求值1.问题描述(1)表达式求值问题表达式是数据运算的基本形式。
人们的书写习惯是中缀式,如:11+22*(7-4)/3。
中缀式的计算按运算符的优先级及括号优先的原则,相同级别从左到右进行计算。
表达式还有后缀式(如:22 7 4 - * 3 / 11 +)和前缀式(如:+ 11 / * 22 –7 4 3)。
后缀表达式和前缀表达式中没有括号,给计算带来方便。
如后缀式计算时按运算符出现的先后进行计算。
本设计的主要任务是进行表达式形式的转换及不同形式的表达式计算。
2.数据结构设计(1)表达式求值问题由于表达式中有字符与数字两种类型,故定义结点一个标志域data,标志结点存储的为字符data=2还是数字data=1,再寻找结点中对应的存储位置,读取数字域data1,字符域data2。
而在前缀表达式时,存在表达式逆序,因表达式类型不统一,用栈逆序极不方便,选择构建双向链表,存储表达式。
typedef struct Node //定义存储中缀表达式的结点类型{int data;int data1;char data2;struct Node *next;}Lnode;typedef struct Node2 //定义存储前缀表达式的结点类型{int data;int data1;char data2;struct Node2 *next;struct Node2 *prior;}Lnode2;3.运行、测试与分析(1)表达式求值问题(1)按提示输入中缀表达式,如图1.1所示。
如输入中缀表达式不正确,提示输入有误,如图1.2,1.3所示。
图1.1图1.2图1.3(2)选择表达式转换并求值方式。
按“1”选择中缀表达式求值,如图1.4所示。
图1.4(3)按“2”选择中缀表达式转变为后缀表达式并求值,如图1.5所示。
图1.5(4)按“3”选择中缀表达式转变为前缀表达式并求值,如图1.6所示。
中缀表达式转前后缀表达式的⼿⼯求法⽆论是⾯试还是考研,数据结构中都会出现⼀类题型,就是已知中缀表达式求其前缀表达式或者后缀表达式。
这类题型多遇见⼏个,多做⼏个掌握以下⽅法,就没问题了。
先说⼀个⽐较简单的,个⼈还是⽐较倾向于这种的⼆叉树表⽰法:表达式A*B:左⼦树为表达式A,右⼦树为表达式B,可以先求左⼦树所表⽰的表达式的值,再求右⼦树所表⽰的表达式的值,最后⼆者相乘。
注意,所画出的⼆叉树,它的叶⼦节点为数值,⾮叶⼦节点是运算符。
画出⼆叉树以后,依次进⾏前序遍历和后序遍历,可以得出前缀表达式和后缀表达式。
例如美团曾经出过的⼀个笔试题⽬:解答:根据题⽬,我们可以画出⼆叉树,如图所⽰:然后根据后序遍历,即可得出后缀表达式,前序遍历即可得出前缀表达式~另外⼀种办法是利⽤栈进⾏操作,利⽤栈进⾏操作的这⼀种办法和许多书上不太⼀样,这是我在其他⼤⽜的博客⾥学习的,那么在这⾥附上⼤⽜写的博客的⽹址:顺带把⼤⽜总结的内容⾃⼰⼩结⼀下:将中缀表达式转换为前缀表达式:遵循以下步骤:(1) 初始化两个栈:运算符栈S1和储存中间结果的栈S2;(2) 从右⾄左扫描中缀表达式;(3) 遇到操作数时,将其压⼊S2;(4) 遇到运算符时,⽐较其与S1栈顶运算符的优先级:(4-1) 如果S1为空,或栈顶运算符为右括号“)”,则直接将此运算符⼊栈;(4-2) 否则,若优先级⽐栈顶运算符的较⾼或相等,也将运算符压⼊S1;(4-3) 否则,将S1栈顶的运算符弹出并压⼊到S2中,再次转到(4-1)与S1中新的栈顶运算符相⽐较;(5) 遇到括号时:(5-1) 如果是右括号“)”,则直接压⼊S1;(5-2) 如果是左括号“(”,则依次弹出S1栈顶的运算符,并压⼊S2,直到遇到右括号为⽌,此时将这⼀对括号丢弃;(6) 重复步骤(2)⾄(5),直到表达式的最左边;(7) 将S1中剩余的运算符依次弹出并压⼊S2;(8) 依次弹出S2中的元素并输出,结果即为中缀表达式对应的前缀表达式。
一、概述在数学运算中,算术表达式是一种描述数学运算的方式,常通过符号和数字的组合表示特定的计算过程。
前缀算术表达式是一种特殊的算术表达式,它的操作符位于操作数之前,例如"+ 3 4"表示加法运算,操作数为3和4。
前缀表达式的计算机实现在计算机科学和编程中具有广泛的应用。
本文将介绍前缀算术表达式的转换和表达式的计算方法。
二、前缀算术表达式转换1. 中缀表达式转前缀表达式中缀表达式是我们常见的数学表达式,例如"3 + 4 * 5"。
中缀表达式转前缀表达式的关键在于将操作符移动到操作数之前。
转换方法如下:- 从右向左遍历中缀表达式的每个字符。
- 如果是操作数,则直接输出。
- 如果是运算符,则与运算符栈顶的运算符比较优先级,如果优先级高于栈顶运算符,则入栈;否则弹出栈顶运算符并输出,直到满足条件入栈。
- 遍历完成后,弹出栈中所有运算符并输出,即得到前缀表达式。
中缀表达式"3 + 4 * 5"转换为前缀表达式为"+ 3 * 4 5"。
2. 后缀表达式转前缀表达式后缀表达式是操作数位于操作符之前的算术表达式,例如"3 4 + 5 *"。
后缀表达式转前缀表达式的方法如下:- 从左向右遍历后缀表达式的每个字符。
- 如果是操作数,则入栈。
- 如果是运算符,则弹出栈顶的两个操作数,并将运算符与操作数组合,得到新的前缀表达式,并将结果入栈。
- 遍历完成后,栈内剩下的元素即为前缀表达式。
后缀表达式"3 4 + 5 *"转换为前缀表达式为"* + 3 4 5"。
3. 中缀表达式转后缀表达式再转前缀表达式有时候,我们将中缀表达式转换为后缀表达式,然后再将后缀表达式转换为前缀表达式。
这种方法的具体步骤如下:- 将中缀表达式转换为后缀表达式。
- 将后缀表达式转换为前缀表达式。
这种方法的优点在于简化了中缀表达式到前缀表达式的转换过程。
中缀、前缀、后缀表达式的运算 中缀表达式,就是在表达式中,操作符在操作数的中间,⽐如 (1+2)*3,+和*在1, 2, 3的中间。
前缀表达式,就是操作符在操作数的前⾯,⽐如 +12,+在1, 2的前⾯。
后缀表达式,就是操作符在操作数的后⾯,⽐如 12+,+在1, 2的后⾯。
为什么会有这么多表达式呢?它们⽬的不同。
中缀表达式,便于我们书写,也符合我们的阅读习惯,在计算机程序中,都是写中缀表达式,但它却不利于计算机进⾏算术计算,因为涉及到优先级和括号。
前缀表达式和后缀表达式中没有括号,并且运算符的优先级也通过它们在表达式中的顺序体现出来了,有利于计算机进⾏算术运算。
前缀表达式也叫波兰表达式,因为它是由⼀个波兰⼈发明的,相应的,后缀表达式也称为逆波兰表达式。
举个例⼦来说明⼀下三者的运算过程,假设计算(3+4)*5-6 使⽤中缀表达式进⾏计算 1,创建两个栈,⼀个是数字栈,⽤来存放操作数,⼀个是字符栈,⽤来存放操作符。
2,算术表达式是⼀个字符串,从左到右循环遍历表达式,依次取出每⼀个字符。
当取出的字符是操作数时,⼊数字栈。
当取出的字符是操作符时,这时还要看操作符的优先级和字符栈是否为空 如果字符栈为空,直接把取出的字符放⼊到字符栈中。
如果字符栈不为空,则要⽐较字符的优先级 如果取出的字符的优先级⼤于等于字符栈中栈顶的字符的优先级,直接把取出的字符放⼊到字符栈中。
如果取出的字符的优先级⽐字符栈中栈顶的字符的优先级低,则要循环进⾏如下操作,直到取出的字符的优先级⼤于等于字符栈中栈顶字符的优先级或者栈为空,此时,把取出的字符放⼊到字 符栈中。
如下操作就是: 1,从字符栈中弹出操作符 2,从数字栈中弹出两个操作数。
3,操作数结合操作符进⾏计算,要注意操作数顺序,后⾯pop出的数在求值的表达式中是前⾯的操作数,尤其是在做减法的时候 4,计算出的值放⼊到数字栈。
为什么要⽐较操作符的优先级呢?因为要确定操作数属于哪个操作符,相邻两个操作符之间的数字是共享的。
前缀中缀后缀表达式转换题目前缀中缀后缀表达式转换1. 前缀中缀后缀表达式的定义在计算机科学中,前缀、中缀和后缀表达式是表示数学表达式的三种方式。
前缀表达式又称为波兰式,中缀表达式即我们常见的普通表达式,后缀表达式又称为逆波兰式。
它们在不同的场景中有不同的优势,比如计算速度、存储空间等。
2. 前缀中缀后缀表达式之间的转换将一个中缀表达式转换为前缀或后缀表达式,并不是一件简单的事情。
在这个过程中,需要考虑运算符的优先级、括号的处理等问题。
而根据不同的需求,转换的步骤也会有所不同。
在转换的过程中,我们需要使用栈这种数据结构来辅助完成这一复杂的操作。
3. 转换过程详解让我们来看看将中缀表达式转换为后缀表达式的过程。
在这个过程中,我们需要遍历中缀表达式中的每个元素,并根据元素的类型来决定下一步的操作。
当遇到数字时,我们直接输出;当遇到操作符时,我们需要根据其优先级来选择是将其入栈还是进行出栈操作。
当中缀表达式遍历完成后,我们需要将栈中剩余的操作符全部弹出,以获得最终的后缀表达式。
4. 前缀表达式的应用前缀表达式的一个重要应用是在计算机的编译原理和语法分析中。
通过将中缀表达式转换为前缀表达式,可以方便地进行语法分析和计算,同时也减少了括号的使用。
在实际的编程中,前缀表达式也经常被用来表示和计算数学表达式。
5. 个人观点和理解我个人认为,对于前缀中缀后缀表达式的转换,我们需要理解其基本原理和规则,并且要在实际的问题中多加练习和实践,才能真正掌握其应用技巧。
另外,对于不同的转换方法,我们也需要根据具体情况来选择最合适的方式,才能更好地发挥其优势。
6. 总结在本文中,我对前缀中缀后缀表达式的定义、转换过程以及应用进行了详细的阐述。
通过对这一主题的深入探讨,我希望读者能够对这一话题有更深刻的理解,并在实际应用中加以运用。
我也分享了自己的一些观点和理解,希望能够引发更多人对这一主题的讨论和思考。
结束语:希望本文能够对您有所帮助,也欢迎在评论区分享您的想法和看法。
中缀表达式转后缀表达式(代码实现)及前缀表达式思路补充 后缀表达式适合计算机式的计算,因此在开发中,我们需要将中缀表达式转为后缀表达式。
三种表达式 这⾥再次区分⼀下前缀表达式、中缀表达式、后缀表达式(以(3+4)*5-6为例) 中缀表达式就是我们正常遇见的(3+4)*5-6这样的式⼦ 前缀表达式⼜称为波兰式,其运算符是在数之前 中缀表达式转前缀表达式思路:从右⾄左扫描表达式,遇到数字时,将数字压⼊堆栈,遇到运算符时,弹出栈顶的两个数,⽤运算符对它们做相应的计算(栈顶元素 top 次顶元素),并将结果⼊栈;重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果 例如:- * + 3 4 5 61. 从右⾄左扫描,将6、5、4、3压⼊堆栈2. 遇到+运算符,因此弹出3和4(3为栈顶元素,4为次顶元素,注意与后缀表达式做⽐较),计算出3+4的值,得7,再将7⼊栈3. 接下来是×运算符,因此弹出7和5,计算出7×5=35,将35⼊栈4. 最后是-运算符,计算出35-6的值,即29,由此得出最终结果中缀表达式转后缀表达式具体步骤 1).初始化两个栈:运算符栈s1和存储中间结果的栈s2; 2).从左到右扫描中缀表达式; 3).遇到操作数时,将其压⼊s2; 4).遇到运算符时,⽐较其与s1栈顶运算符的优先级; 1.如果s1为空,或栈顶运算符为左括号"(",则直接将此运算符⼊栈 2.否则,若优先级⽐栈顶运算符的⾼,也将运算符压⼊s1 3.否则,将s1栈顶的运算符弹出并压⼊s2中,再次转到(4-1)与s1中新的栈顶运算符相⽐较; 5)遇到括号时: (1)如果时左括号"(",则直接压⼊s1 (2)如果是右括号")",则依次弹出s1栈顶的运算符,并压⼊s2,直到遇到左括号为⽌,此时将这⼀对括号丢弃 6)重复2⾄5,直到表达式的最右边 7)将s1中剩余的运算符依次弹出并压⼊s2 8)依次弹出s2中的元素并输出,结果的逆序为中缀表达式对应的后缀表达式举例说明 将中缀表达式"1+((2+3)*4)-5"转换为后缀表达式为:"1 2 3 + 4 * + 5 -"过程如下:举例代码package com.atxihua;import java.util.ArrayList;import java.util.List;import java.util.Stack;public class PolandNotation1 {public static void main(String[] args) {//完成将⼀个中缀表达式转成后缀表达式的功能//1+((2+3)*4)-5转成1 2 3 + 4 * + 5 -//因为直接对str进⾏操作,不⽅便,因此,先将1+((2+3)*4)-5存⼊对应的list//即1+((2+3)*4)-5=》ArrayList[1,+,(,(,2,+,3,),*,4),-,5]//将得到的中缀表达式对应的List=》后缀表达式对应的List//即ArrayList[1,+,(,(,2,+,3,),*,4),-,5]=>ArrayList[1,2,3,+,4,*,+,5,-]String expression="1+((2+3)*4)-5";List<String> infixExpreesionList=toInfixExpreesionList(expression);System.out.println("中缀表达式="+infixExpreesionList);//ArrayList[1,+,(,(,2,+,3,),*,4),-,5] List<String> suffixExpreesionList=parseSuffixExpreesionList(infixExpreesionList);System.out.println("后缀表达式="+suffixExpreesionList);//ArrayList[1,2,3,+,4,*,+,5,-]System.out.println("结果为:"+calculate(suffixExpreesionList));}//将中缀表达式转成对应的listprivate static List<String> toInfixExpreesionList(String s) {//定义⼀个List,存放中缀表达式对应的内容List<String> ls=new ArrayList<String>();int i=0;//⽤于遍历中缀表达式字符串String str;//⽤于拼接多位数char c;//每遍历到⼀个字符,就放到cdo{//如果c是⼀个⾮数字,我们需要加⼊到lsif((c=s.charAt(i))<48||(c=s.charAt(i))>57){ls.add(""+c);i++;}else {//如果是⼀个数,需要考虑多位数str="";//先将str设置为0[48]-9[57]while (i<s.length()&&(c=s.charAt(i))>=48&&(c=s.charAt(i))<=57){str+=c;//拼接i++;}ls.add(str);}}while (i<s.length());return ls;}//将得到的中缀表达式对应的List=>后缀表达式private static List<String> parseSuffixExpreesionList(List<String> ls) {//定义两个栈Stack<String> s1=new Stack<String>();//符号栈//说明:s2这个栈,在整个转换过程中,没有pop操作,⽽且后⾯需要逆序输出,就不使⽤栈,直接使⽤list存储//Stack<String> s2=new Stack<String>();//数栈List<String> s2=new ArrayList<String>();//存储中间结果的list//遍历lsfor(String item:ls){//如果是⼀个数,加⼊s2if(item.matches("\\d+")){s2.add(item);}else if(item.equals("(")){s1.push(item);}else if(item.equals(")")){//如果是右括号")",则依次弹出s1栈顶的运算符,并压⼊s2,直到遇到左括号为⽌,此时,将这⼀对括号丢弃while (!s1.peek().equals("(")){s2.add(s1.pop());}s1.pop();//将 ( 弹出s1栈,消除⼩括号}else {//当item的优先级⼩于等于s1栈顶运算符,将s1栈顶运算符弹出并加⼊到s2,直到遇到左括号为⽌,此时将这⼀对括号丢弃 //我们缺少⼀个⽐较优先级⾼低的⽅法while (s1.size()!=0&& Operation.getValue(s1.peek())>=Operation.getValue(item)){s2.add(s1.pop());}//还需要将item压⼊栈s1.push(item);}}//将s1中剩余的运算符依次弹出并加⼊s2while (s1.size()!=0){s2.add(s1.pop());}return s2;//}private static int calculate(List<String> ls) {//创建⼀个栈Stack<String> stack = new Stack<String>();//遍历lsfor(String item:ls){//使⽤正则表达式来取数if(item.matches("\\d+")){//匹配多为数//⼊栈stack.push(item);}else {//pop出两个数并运算,再⼊栈int num2=Integer.parseInt(stack.pop());int num1=Integer.parseInt(stack.pop());int res=0;if(item.equals("+")){res=num1+num2;}else if(item.equals("-")){//注意num2⽐num1先出栈,所以是num1-num2res=num1-num2;}else if(item.equals("/")){res=num1/num2;}else if(item.equals("*")){res= num1*num2;}else {throw new RuntimeException("运算符有误");}//把res⼊栈stack.push(""+res);}}//最后留在stack中的数据即运算结果return Integer.parseInt(stack.pop());}}//编写⼀个类Operation可以返回⼀个运算符,对应的优先级class Operation{private static int Add=1;private static int SUB=1;private static int MUL=2;private static int DIV=2;//写⼀个⽅法,返回对应的优先级数字public static int getValue(String operation){int result=0;if(operation.equals("+")){result=Add;}else if(operation.equals("-")){result=SUB;}else if(operation.equals("*")){result=MUL;}else if(operation.equals("/")){result=DIV;}else {System.out.println("不存在该运算符");}return result;}}运⾏结果因为我们在写代码时,遇到左括号时,压⼊栈中,在遇到右括号时在处理,因此,计算机将左括号当作运算符处理,在处理逻辑中只对加减乘除进⾏了处理,相信这点很容易理解,就不再处理了。
前缀中缀后缀表达式转换题目摘要:1.表达式的基本概念2.前缀、中缀和后缀表达式的定义3.表达式转换的方法和应用4.总结正文:一、表达式的基本概念在计算机科学中,表达式是指用运算符连接操作数的式子。
表达式可以是数字、字符、变量或者函数调用等。
根据运算符的优先级和结合性,表达式可以按照特定的顺序进行计算,得到一个结果。
二、前缀、中缀和后缀表达式的定义1.前缀表达式:前缀表达式是一种特殊的表达式,它的每个操作都只有一个前驱。
也就是说,我们可以从左到右依次计算每个操作的值,而不需要考虑运算符的优先级。
例如,表达式"a+b"就是一个前缀表达式。
2.中缀表达式:中缀表达式是包含了运算符优先级的表达式。
它的每个操作都可以有零个或多个前驱和后继。
例如,表达式"a*b+c"就是一个中缀表达式。
3.后缀表达式:后缀表达式是一种特殊的中缀表达式,它的每个操作符都有一个后驱,但没有前驱。
后缀表达式的计算顺序是按照运算符的顺序进行的,这样可以避免运算符优先级的问题。
例如,表达式"ab+c"就是一个后缀表达式。
三、表达式转换的方法和应用表达式转换是一种将一种类型的表达式转换为另一种类型的表达式的操作。
常见的表达式转换有前缀表达式转换为中缀表达式,中缀表达式转换为后缀表达式等。
1.前缀表达式转换为中缀表达式:可以通过递归地将前缀表达式的每个操作转换为中缀表达式来实现。
例如,对于前缀表达式"a+b",我们可以转换为中缀表达式"a,+,b"。
2.中缀表达式转换为后缀表达式:可以通过将中缀表达式的每个操作转换为后缀表达式,并将运算符转换为后缀表达式的操作符来实现。
例如,对于中缀表达式"a*b+c",我们可以转换为后缀表达式"ab,+,c"。
表达式转换在编译原理、计算理论等领域有广泛的应用。
例如,在编译器中,将源代码转换为目标代码的过程中,就需要进行表达式的转换和优化。