900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > 中缀 后缀 前缀表达式

中缀 后缀 前缀表达式

时间:2021-10-13 14:34:24

相关推荐

中缀 后缀 前缀表达式

一.简介

对于1+((2*3)-4)/2 的数学表达式怎么求值?

分析:

数学表达式求值有优先级,不能简单的从左往右依次计算, 需要从优先级高的开始计算

中缀表达式是一种通用的算术或逻辑公式表示方法,操作符以中缀形式处于操作数的中间。 人的大脑很容易理解与分析中缀表达式,但计算机对中缀表达式却是很复杂的,计算前缀或后缀表达式的值非常简单,因此计算表达式的值时,通常需要先将中缀表达式转换为前缀或后缀表达式,然后再进行求值。

1.前缀和后缀表达式已经内在地包含运算顺序,因此不用括号来确定优先级

2.前缀和后缀表达式适合计算机计算 ,前缀表达式和后缀表达式计算的时候都是从一个方向扫描表达式,遇到数字压入栈,遇到运算符弹出栈顶的两个数进行运算并将结果入栈,重复直到结束

二.中缀表达式

运算符在数值之间就是中缀表达式,如1+((2*3)-4)/2,数学表达式天然就是中缀表达式。

从左往右遍历每个字符:

1.字符是数值就入栈到数值栈中

2.字符为运算符就和上一个运算符比较优先级(没有就直接入栈,左括号"(" 直接入栈);小于上一个运算符优先级就出栈2个数值,出栈1个运算符并计算结果入栈,再入栈当前运算符;如果为")" 则计算括号内的数值并入栈,直到符号栈符号为"("

数学表达式解析完成后:

从数值栈出栈2个数值,符号栈出栈一个符号,计算结果并入栈,直到栈中只有一个数值

流程图:

实现:

MathExpression.java

package com.vincent;import java.util.HashMap;import java.util.Map;public class MathExpression {/*** 计算中缀表达式* @param exp 待计算的中缀表达式* @return*/public static int calcExpression(String exp){exp = exp.replace(" ","");java.util.LinkedList<Integer> numStack = new java.util.LinkedList<>();java.util.LinkedList<String> opStack = new java.util.LinkedList<>();for(int i=0;i<exp.length();i++){String ch = exp.substring(i,i+1);if(operPriority(ch) != null){//获取的是运算符if(opStack.size() == 0 || ch.equals("(")){opStack.addLast(ch);}else if(ch.equals(")")){//有括号表达式,计算整个括号表达式结果并入栈while(!opStack.getLast().equals("(")){int num2 = numStack.removeLast();int rst = calc(numStack.removeLast(),num2,opStack.removeLast());numStack.addLast(rst);}//去除"(" 括号opStack.removeLast();}else if(operPriority(opStack.getLast()) < operPriority(ch) && !opStack.getLast().equals("(")){//当前运算符优先级低于上一个运算符优先级int num2 = numStack.removeLast();int rst = calc(numStack.removeLast(),num2,opStack.removeLast());numStack.addLast(rst);opStack.addLast(ch);}else{opStack.addLast(ch);}}else{//获取的是非运算符int pos = i+1;while(pos < exp.length() && operPriority(exp.substring(pos,pos+1)) == null){pos++;}numStack.addLast(Integer.parseInt(exp.substring(i,pos)));i = pos - 1;}}while(numStack.size() > 1){int num2 = numStack.removeLast();int rst = calc(numStack.removeLast(),num2,opStack.removeLast());numStack.addLast(rst);}return numStack.removeLast();}/*** 获取符号优先级,对于运算符其值越低优先级越高,如果是改变运算符优先级的符号(如括号)获取将是负数* @param op* @return*/public static Integer operPriority(String op){Map<String,Integer> opMap = new HashMap<>();opMap.put("+",2);opMap.put("-",2);opMap.put("*",1);opMap.put("/",1);opMap.put("(",-1);opMap.put(")",-1);return opMap.get(op);}public static int calc(int num1,int num2,String op){if(op.equals("+")){return num1 + num2;}else if(op.equals("-")){return num1 - num2;}else if(op.equals("*")){return num1 * num2;}else if(op.equals("/")){return num1 / num2;}throw new IllegalArgumentException();}}

Main.java

package com.vincent;public class Main {public static void main(String[] args) throws Exception{String exp = "1+((2*3)-4)/2";System.out.println(MathExpression.calcExpression(exp));}}

效果:

中缀表达式转为后缀、前缀的思路和计算中缀表达式基本一样,只是中缀转后缀是从字符串头部解析,中缀转前缀是从字符串尾部开始解析。

三.后缀(逆波兰)表达式

MathExpression.java 增加如下方法

/*** 数学表达式转化为后缀(逆波兰)表达式* @param exp 待转化的数学表达式* @return后缀表达式*/public static String toSuffixExpression(String exp){exp = exp.replace("\\s","");java.util.LinkedList<String> exprStack = new java.util.LinkedList<>();java.util.LinkedList<String> opStack = new java.util.LinkedList<>();for(int i=0;i<exp.length();i++){char ch = exp.charAt(i);if(operPriority(ch+"") != null){if(opStack.size() == 0 || ch == '('){opStack.addLast(ch+"");}else if(ch == ')'){while(!opStack.getLast().equals("(")) {exprStack.addLast(opStack.removeLast());}//出栈左括号"("opStack.removeLast();}else if(operPriority(opStack.getLast()) < operPriority(ch+"") && !opStack.getLast().equals("(")){//符号栈顶符号优先级较高exprStack.addLast(opStack.removeLast());opStack.addLast(ch+"");}else{opStack.addLast(ch+"");}}else{int pos = i+1;//数字字符0-9编码为48-57while(pos < exp.length() && exp.charAt(pos)>=48 && exp.charAt(pos)<=57){pos++;}exprStack.addLast(exp.substring(i,pos));i = pos -1;}}while(opStack.size() > 0){exprStack.addLast(opStack.removeLast());}StringBuilder rst = new StringBuilder();while(exprStack.size() > 0){rst.append(exprStack.removeLast()).append(" ");}rst.reverse();return rst.toString().trim();}public static int calcSuffixExpression(String exp){String[] parts = exp.split(" ");java.util.LinkedList<Integer> stack = new java.util.LinkedList<>();for(String part : parts){if(operPriority(part) == null){stack.addLast(Integer.parseInt(part));}else{//第一个出栈的是操作数2int num2 = stack.removeLast();int rst = calc(stack.removeLast(),num2,part);stack.addLast(rst);}}return stack.removeLast();}

Main.java

package com.vincent;public class Main {public static void main(String[] args) throws Exception{String exp = "1+((2*3)-4)/2";System.out.println(MathExpression.calcExpression(exp));System.out.println("后缀表达式:" + MathExpression.toSuffixExpression(exp));System.out.println(MathExpression.calcSuffixExpression(MathExpression.toSuffixExpression(exp)));}}

效果:

四.前缀表达式

前缀表达式是后缀表达式的反向遍历操作,即从表达式尾部向头部开始解析操作。

MathExpression.java 增加如下方法:

/*** 解析数学表达式为前缀表达式* @param exp* @return*/public static String toPrefixExpression(String exp){exp = exp.replace("\\d","");java.util.LinkedList<String> exprStack = new java.util.LinkedList<>();java.util.LinkedList<String> opStack = new java.util.LinkedList<>();for(int i=exp.length()-1;i>=0;i--){char ch = exp.charAt(i);if(operPriority(ch+"") != null){if(opStack.size() == 0 || ch == ')'){opStack.addLast(ch+"");}else if(ch == '('){while(!opStack.getLast().equals(")")){exprStack.addLast(opStack.removeLast());}//移除符号栈中的右括号")"opStack.removeLast();}else if(operPriority(opStack.getLast()) < operPriority(ch+"") && !opStack.getLast().equals(")")){exprStack.addLast(opStack.removeLast());opStack.addLast(ch+"");}else{opStack.addLast(ch+"");}}else{int pos = i - 1;while(pos >=0 && exp.charAt(pos) >= 48 && exp.charAt(pos) <= 57){pos--;}exprStack.addLast(exp.substring(pos+1,i+1));i = pos + 1;}}while(opStack.size() > 0){exprStack.addLast(opStack.removeLast());}StringBuilder rst = new StringBuilder();while(exprStack.size() > 0){rst.append(exprStack.removeLast()).append(" ");}return rst.toString().trim();}/*** 计算前缀表达式* @param exp 前缀表达式* @return*/public static int calcPrefixExpression(String exp){String[] parts = exp.split(" ");java.util.LinkedList<Integer> stack = new java.util.LinkedList<>();for(int i=parts.length-1;i>=0;i--){if(operPriority(parts[i]) == null){stack.addLast(Integer.parseInt(parts[i]));}else{//第一个出栈的是第一个操作数int num1 = stack.removeLast();int rst = calc(num1,stack.removeLast(),parts[i]);stack.addLast(rst);}}return stack.removeLast();}

Main.java

package com.vincent;public class Main {public static void main(String[] args) throws Exception{String exp = "1+((2*3)-4)/2";System.out.println(MathExpression.calcExpression(exp));System.out.println("后缀表达式:" + MathExpression.toSuffixExpression(exp));System.out.println(MathExpression.calcSuffixExpression(MathExpression.toSuffixExpression(exp)));System.out.println("前缀表达式:" + MathExpression.toPrefixExpression(exp));System.out.println(MathExpression.calcPrefixExpression(MathExpression.toPrefixExpression(exp)));}}

效果:

五.总结

不管是中缀、后缀、前缀表达式在解析运算符时都需要和上一个运算符比较优先级,只有在优先级小于上一个运算符优先级时才能知道上一个运算符优先级较高,故才不会影响计算的优先级

前缀和后缀表达式已经内在地包含运算顺序,因此不用括号来确定优先级

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。