我们平常书写的四则运算表达式属于中缀表达式,形式为"9+(3-1)*3+10/2",因为所有的运算符号都在两操作数之间,所以称为中缀表达式。我们使用中缀表达式来计算表达式的值,不过这种形式并不适合计算机求解。接下来,我们将中缀表达式转化为后缀表达式,所谓的后缀表达式就是操作符位于操作数后面的不包含括号的算数表达式,也叫做逆波兰表达式。
1)首先介绍一种人工的转化方法(http://www.cnblogs.com/MichaelYin/archive/2012/05/02/2479248.html)。以"9+(3-1)*3+10/2"为例,按照运算的规则,找出首先计算的部分,这部分包含两个操作数和一个操作符,将操作符移动到两个操作数右侧,这就完成了第一部分的转换,将这部分看作一个操作数,按照运算规则,以相同的方法转换,转换过程如下:
2)还可以利用二叉树求得后缀表达式,首先利用中缀表达式构造二叉树,数字是叶子节点,操作符为根节点。每次找到“最后计算”的运算符,作为当前根节点,运算符左侧表达式作为左节点,右侧表达式作为右节点,然后递归处理(http://www.xuebuyuan.com/388108.html)。9+(3-1)*3+10/2对应的二叉树的构造过程如下图所示:
此二叉树做后序遍历就得到了后缀表达式。
3)还可以利用栈来实现中缀表达式转化为后缀表达式。转化方法如下所述:
a.从左向右扫描表达式,如果是数字就输出,否则转b。
b.如果当前扫描的字符是")",则栈顶元素出栈并输出一直到栈顶元素为"(",然后删除栈顶元素"(",并不输出。
c.如果扫描的字符或者栈顶元素是“(”,扫描的字符直接入栈。即使扫描的字符是")"也不会入栈,因为如果是")",会出栈至栈顶元素是"("。
d.如果扫描字符是"+"或者"-",则一直出栈至栈顶元素为"+"或者"-"或者"("。如果最终栈顶元素是"(",则扫描操作符直接入栈,否则,弹出栈顶元素,然后扫描的操作符再入栈。
e.如果扫描的操作符是"*"或者"/",如果栈顶元素是同优先级的"*"或者"/",首先将栈顶元素出栈,然后扫描的操作符入栈。否则,直接入栈。
f.扫描完成整个表达式之后,如果栈内还有元素,则依次全部出栈。
对应的代码如下,首先是栈的定义,然后是reversePlishNotation2转换方法,表达式输入格式为字符串,操作数和运算符以及小括号以空格分隔:
1 mport java.util.Arrays; 2 import java.util.HashSet; 3 import java.util.Random; 4 import java.util.Set; 5 6 /** 7 * Created by hfz on 2016/7/30. 8 */ 9 public class Stack_Array implements Stack { 10 public static int CAPACITY=40; 11 protected int capacity; 12 protected int top=-1; 13 protected Object[] S; 14 public Stack_Array(int capacity){ 15 this.capacity=capacity; 16 S=new Object[capacity]; 17 } 18 public Stack_Array(){ 19 this(CAPACITY);//一般情况下,使用this.方法名调用自身方法,不过在构造方法里可以使用this()的形式调用其他构造函数 20 } 21 public void push(Object ele) throws ExceptionStackFull { 22 if(getSize()==CAPACITY){ 23 throw new ExceptionStackFull("栈溢出!"); 24 } 25 S[++top]=ele; 26 } 27 public Object pop() throws ExceptionStackEmpty{ 28 if(isEmpty()){ 29 throw new ExceptionStackEmpty("栈为空,不能出栈!"); 30 } 31 Object ele=S[top]; 32 S[top--]=null; 33 return ele; 34 } 35 36 public Object top() throws ExceptionStackEmpty{ 37 if(isEmpty()){ 38 throw new ExceptionStackEmpty("栈为空,没有栈顶元素!"); 39 } 40 return S[top]; 41 } 42 public boolean isEmpty(){ 43 return (top<0); 44 } 45 public int getSize(){ 46 return top+1; 47 } 48 49 public static void main(String [] args){ 50 String s="9 + ( 3 - 1 ) * 3 + 10 / 2"; 51 String s1="20 * ( ( 2.44 - 1.8 ) / 0.4 + 0.15 )"; 52 53 System.out.println(reversePlishNotation2(s)); 54 System.out.println(reversePlishNotation2(s1)); 55 inverseArray(); 56 57 58 } 59 60 /* 61 中缀表达式以#作为结束标志,以空格分割各个元素。 62 中缀表达式转化为后缀表达式(逆波兰表达式)的方法 63 1.从左向右扫描中缀表达式,遇到数字就直接输出。遇到运算符,如果其优先级高于栈顶元素或栈为空,入栈。如果低于栈顶 64 元素优先级,则栈顶元素出栈输出,一直到栈顶元素的优先级低于或等于此运算符。如果两个运算符优先级相同的话,弹出栈顶元素,然后再将运算符入栈。 65 66 2.对于小括号做另外处理,如果是左括号“(”,直接入栈。右括号")"的话,栈顶元素出栈输出,一直到栈顶元素为“(”,将“(”出栈 67 ,不过并不输出。右括号“)”并不入栈,直接丢弃。 68 3.扫描完成之后,如果栈中还有元素,依次输出。 69 */ 70 public static String reversePlishNotation2(String str){ 71 Stack_Array notationStack = new Stack_Array(); 72 String[] expressionString=str.split(" "); 73 int expressionLength = expressionString.length; 74 String currentString = "0"; 75 StringBuilder sb = new StringBuilder(); 76 Set<String> notationSet=new HashSet<>(); 77 notationSet.add("+"); 78 notationSet.add("-"); 79 notationSet.add("*"); 80 notationSet.add("/"); 81 notationSet.add("("); 82 notationSet.add(")"); 83 String topElement="null"; 84 for (int i = 0; i <expressionLength ; i++) { 85 currentString=expressionString[i]; 86 if(!notationSet.contains(currentString)){ 87 sb.append(currentString).append(" "); 88 } 89 else{//不是数字,而是操作符 90 try{ 91 topElement=(String)notationStack.top(); 92 } 93 catch (ExceptionStackEmpty ex){ 94 notationStack.push(currentString); 95 continue; 96 } 97 if(currentString.equals(")")){//当前扫描的字符是")",则操作符出栈至栈顶元素是"(",同时添加操作符,最后删除"("。 98 while(!topElement.equals("(")) { 99 sb.append(topElement).append(" "); 100 try { 101 notationStack.pop(); 102 topElement = (String) notationStack.top(); 103 } 104 catch (ExceptionStackEmpty ex){ 105 break; 106 } 107 } 108 if(topElement.equals("(")){ 109 notationStack.pop(); 110 } 111 } 112 else if(currentString.equals("(")||topElement.equals("(")){//如果扫描字符或者栈顶字符是"(",则扫描字符直接入栈。 113 // 即使扫描字符是")"也不会入栈,因为上面一层的判断条件,要求出栈至"("。 114 notationStack.push(currentString); 115 } 116 else if(currentString.equals("+")||currentString.equals("-")){ 117 //如果扫描字符是"+"或者"-",则一直出栈至栈顶元素为"+"或者"-"或者"("。如果最终栈顶元素是"(",则扫描操作符直接入栈,否则,弹出栈顶 118 //元素,然后扫描的操作符再入栈。 119 while (!topElement.equals("+")&&!topElement.equals("-")&&!topElement.equals("(")){ 120 sb.append(topElement).append(" "); 121 try{ 122 notationStack.pop(); 123 topElement=(String)notationStack.top(); 124 } 125 catch (ExceptionStackEmpty ex){ 126 break; 127 } 128 } 129 if(topElement.equals("+")||topElement.equals("-")){ 130 sb.append((String)notationStack.pop()).append(" "); 131 } 132 notationStack.push(currentString); 133 } 134 else if(currentString.equals("*")||currentString.equals("/")){ 135 //如果扫描的操作符是"*"或者"/",如果栈顶元素是同优先级的"*"或者"/",首先将栈顶元素出栈,然后扫描的操作符入栈。否则,直接入栈。 136 if(topElement.equals("*")||topElement.equals("/")){ 137 sb.append((String) notationStack.pop()).append(" "); 138 } 139 notationStack.push(currentString); 140 } 141 } 142 } 143 //扫描完成整个表达式之后,如果栈内还有元素,则依次全部出栈。 144 while (!notationStack.isEmpty()){ 145 sb.append(notationStack.pop()).append(" "); 146 } 147 return sb.toString(); 148 } 149 150 151 } 152 class ExceptionStackFull extends RuntimeException{ 153 public ExceptionStackFull(String err){ 154 super(err); 155 } 156 }