中缀表达式转后缀表达式(代码实现)及前缀表达式思路补充
后缀表达式适合计算机式的计算,因此在开发中,我们需要将中缀表达式转为后缀表达式。
三种表达式
这里再次区分一下前缀表达式、中缀表达式、后缀表达式(以(3+4)*5-6为例)
中缀表达式就是我们正常遇见的(3+4)*5-6这样的式子
前缀表达式又称为波兰式,其运算符是在数之前
中缀表达式转前缀表达式思路:从右至左扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素 top 次顶元素),并将结果入栈;重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果
例如:- * + 3 4 5 6
- 从右至左扫描,将6、5、4、3压入堆栈
- 遇到+运算符,因此弹出3和4(3为栈顶元素,4为次顶元素,注意与后缀表达式做比较),计算出3+4的值,得7,再将7入栈
- 接下来是×运算符,因此弹出7和5,计算出7×5=35,将35入栈
- 最后是-运算符,计算出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)); } //将中缀表达式转成对应的list private static List<String> toInfixExpreesionList(String s) { //定义一个List,存放中缀表达式对应的内容 List<String> ls=new ArrayList<String>(); int i=0;//用于遍历中缀表达式字符串 String str;//用于拼接多位数 char c;//每遍历到一个字符,就放到c do{ //如果c是一个非数字,我们需要加入到ls if((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 //遍历ls for(String item:ls){ //如果是一个数,加入s2 if(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中剩余的运算符依次弹出并加入s2 while (s1.size()!=0){ s2.add(s1.pop()); } return s2;// } private static int calculate(List<String> ls) { //创建一个栈 Stack<String> stack = new Stack<String>(); //遍历ls for(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-num2 res=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; } }
运行结果
因为我们在写代码时,遇到左括号时,压入栈中,在遇到右括号时在处理,因此,计算机将左括号当作运算符处理,在处理逻辑中只对加减乘除进行了处理,相信这点很容易理解,就不再处理了。