对于表达式计算问题可以分成3部分,输入为中缀表达式,输出为:(1)后缀表达式;(2)计算结果;(3)表达式树,其中后面两个问题的求解依赖于前1个问题的求解。本文详细讨论了这三个问题。
1.后缀表达式的生成
读取中缀表达式,根据以下规则处理
(1)当字符不是操作符时,直接输入,否则进行优先级比较
(2)当栈为空时,将操作符直接压入
(3)当操作符为“(”时,直接压入
(4)当操作符为“)”时,依次弹出操作符,直到(为止,但(并不输出
(5)依次比较栈顶与当前操作符的优先级,如栈顶操作符优先级不比当前操作符低的话,则弹出,直到栈顶操作符优先级比当前操作符低或者栈为空为止,然后将当前操作符压入
(6)比较优先级时“(”是个意外,除非当前操作符为“)”,否则不弹出
(7)当读到输入末尾时,将栈内剩余操作符输出
本文不仅讨论了双目运算符,讨论了单目运算符!-,若要添加其他操作符其方法是类似的,使用到的操作符的优先级顺序如下:
操作符 | 优先级 |
( ) | 4 |
! -($在计算结果用来替换负号) | 3 |
* / | 2 |
+ - | 1 |
2.根据后缀表达式计算表达式结果
读取后缀表达式,若不是操作符则压入栈中,若是操作符则从栈中弹出相应个数的数进行计算,并将结果压入栈中
注:本文还讨论了符号-与减号-混用的情况,如果-前面是操作符或者位于第1位,则说明是负号,将其替换成$
3.生成表达式树
生成表达式树的过程和计算表达式类似,不过不是将树压入栈中,而是将相应的节点压入栈中;也不是将结果压入栈中,而是将新的节点压入栈中
4.实现代码
1 /**本文件用于研究表达式树,包括:1.由中缀表达式生成后缀表达式;2.计算后缀表达式 2 * 3.由后缀表达式构造一棵表达式树;4。由后缀表达式生成中缀、前缀表达式 3 * 4 */ 5 6 import java.util.*; 7 8 public class Main { 9 10 public static void main(String[] args) { 11 // 输入中缀表达式,由0-9、+、-、*、/、!符号组成 12 Scanner inSting=new Scanner(System.in); 13 String middleExpression=inSting.nextLine(); 14 //中缀表达式转换为后缀表达式 15 String behindExpression=middleExpToBehindExp(middleExpression); 16 //计算后缀表达式 17 double answer=calculateBehindExp(behindExpression); 18 System.out.println("计算结果为:"+answer); 19 //构造表达式树 20 expTree temp=behindTreeToExpTree(behindExpression); 21 System.out.println("其表达式树为:"); 22 temp.printExpTree(temp, 0); 23 } 24 static String middleExpToBehindExp(String middleExpression){ 25 /*中缀表达式转换为后缀表达式的规则 26 * 1.如果是数字则直接输出,如果是操作符的话就入栈 27 * 2.比较栈里面操作符之间的优先级,将优先级更高或者相等的输出 28 * 3.遇到)前操作符(不弹出 29 * 4.遇到(后弹出到)为止,但(与)都不输出 30 * 5.弹出操作符后,将当期操作符弹出 31 * 6.读到输入末尾时,将栈中元素全部弹出 32 * 7.优先级设置如下,()4;! - 3;/* 2;+ - 1; 33 */ 34 String behindExp=""; 35 Stack<String> helperStack=new Stack<String>(); 36 //添加一个程序处理负号和减号之间的关系,如果-号前是操作符,则认为是负号,将其替换为其他符号,如$,其优先级和其他单目运算符一样 37 for(int i=0;i<middleExpression.length();i++) 38 { 39 //开头为-,说明是负号 40 if(i==0&&middleExpression.charAt(0)=='-') 41 {middleExpression='$'+middleExpression.substring(1);continue;} 42 if(middleExpression.charAt(i)=='-'&&!isNumber(middleExpression.charAt(i-1))) 43 middleExpression=middleExpression.substring(0, i)+'$'+middleExpression.substring(i+1); 44 } 45 System.out.println(middleExpression); 46 47 for(int i=0;i<middleExpression.length();i++){ 48 char nowElement=middleExpression.charAt(i); 49 //如果是不是操作符则直接输出 50 if(isNumber(nowElement)) 51 behindExp=behindExp+nowElement; 52 else 53 { 54 //栈为空,则直接将操作符压入 55 if(helperStack.isEmpty()) 56 {helperStack.push(nowElement+"");continue;} 57 //如果为)则输出直到(为止 58 if(nowElement==')') 59 { 60 int temp=helperStack.search("("); 61 for(int j=0;j<temp-1;j++) 62 behindExp=behindExp+helperStack.pop(); 63 helperStack.pop(); 64 continue; 65 } 66 //判断优先级,输出直到栈内操作符优先级小于当前操作符 67 do{ 68 String temp=helperStack.peek(); 69 //(只有遇到)操作符时才弹出 70 if(temp.charAt(0)=='(') 71 break; 72 if(compareOperateSymbol(temp,nowElement+"")) 73 {behindExp=behindExp+temp;helperStack.pop();} 74 }while(!helperStack.isEmpty()&&compareOperateSymbol(helperStack.peek(),nowElement+"")); 75 //将当前操作符压入 76 helperStack.push(nowElement+""); 77 } 78 } 79 //读到输入末尾,将栈中元素全部弹出 80 if(!helperStack.isEmpty()) 81 {int temp=helperStack.size(); 82 for(int i=0;i<temp;i++) 83 behindExp=behindExp+helperStack.pop();} 84 System.out.println(behindExp); 85 return behindExp; 86 } 87 //判断是不是数字,如果是则返回true,否则返回false 88 static boolean isNumber(char a) 89 { 90 if(a=='/'||a=='*'||a=='+'||a=='-'||a=='!'||a=='('||a==')'||a=='$') 91 return false; 92 else 93 return true; 94 } 95 //比较两个操作符的优先级 96 static boolean compareOperateSymbol(String a,String b){ 97 int temp1=0,temp2=0; 98 switch(a.charAt(0)){ 99 case '(': 100 temp1=4;break; 101 case ')': 102 temp1=4;break; 103 case '!': 104 temp1=3;break; 105 case '*': 106 temp1=2;break; 107 case '/': 108 temp1=2;break; 109 case '+': 110 temp1=1;break; 111 case '-': 112 temp1=1;break; 113 case '$': 114 temp1=3;break; 115 } 116 switch(b.charAt(0)){ 117 case '(': 118 temp2=4;break; 119 case ')': 120 temp2=4;break; 121 case '!': 122 temp2=3;break; 123 case '*': 124 temp2=2;break; 125 case '/': 126 temp2=2;break; 127 case '+': 128 temp2=1;break; 129 case '-': 130 temp2=1;break; 131 case '$': 132 temp2=3;break; 133 } 134 if(temp1>=temp2) 135 return true; 136 else 137 return false; 138 } 139 static double calculateBehindExp(String a){ 140 //构造辅助栈 141 Stack<Double> helperStack=new Stack<Double>(); 142 for(int i=0;i<a.length();i++) 143 { 144 //如果是数字则压入栈中 145 if(isNumber(a.charAt(i))) 146 helperStack.push(Double.parseDouble(a.charAt(i)+"")); 147 //判断表达式是否有问题 148 if(a.charAt(i)=='+'||a.charAt(i)=='-'||a.charAt(i)=='*'||a.charAt(i)=='/') 149 if(helperStack.size()<2) 150 {System.out.println("表达式有误1,请检查");return -1;} 151 if(a.charAt(i)=='$'||a.charAt(i)=='!') 152 if(helperStack.size()<1) 153 {System.out.println("表达式有误2,请检查");return -1;} 154 //如果是操作符则,弹出相应的几个数,进行计算,并将结果压入 155 if(a.charAt(i)=='+') 156 {double temp1=helperStack.pop();double temp2=helperStack.pop(); 157 temp2=temp2+temp1;helperStack.push(temp2); 158 } 159 if(a.charAt(i)=='-') 160 {double temp1=helperStack.pop();double temp2=helperStack.pop(); 161 temp2=temp2-temp1;helperStack.push(temp2); 162 } 163 if(a.charAt(i)=='*') 164 {double temp1=helperStack.pop();double temp2=helperStack.pop(); 165 temp2=temp2*temp1;helperStack.push(temp2); 166 } 167 if(a.charAt(i)=='/') 168 {double temp1=helperStack.pop();double temp2=helperStack.pop(); 169 temp2=temp2/temp1;helperStack.push(temp2); 170 } 171 if(a.charAt(i)=='!') 172 {double temp1=helperStack.pop(); 173 int temp2=1; 174 for(int j=1;j<=temp1;j++) 175 temp2=temp2*j; 176 helperStack.push((double)temp2); 177 } 178 if(a.charAt(i)=='$') 179 {double temp1=helperStack.pop(); 180 helperStack.push(-temp1); 181 } 182 } 183 //计算完之后若栈中还存在多余1个数则报错 184 if(helperStack.size()>1) 185 {System.out.println("表达式有误3,请检查");return -1;} 186 return helperStack.pop(); 187 } 188 static expTree behindTreeToExpTree(String a){ 189 //生成表达式树的过程和计算过程是类似的 190 Stack<expTree> helperStack=new Stack<expTree>(); 191 for(int i=0;i<a.length();i++) 192 { 193 //如果是数字则压入栈中 194 if(isNumber(a.charAt(i))) 195 {expTree temp=new expTree(a.charAt(i));helperStack.push(temp);} 196 //判断表达式是否有问题 197 if(a.charAt(i)=='+'||a.charAt(i)=='-'||a.charAt(i)=='*'||a.charAt(i)=='/') 198 if(helperStack.size()<2) 199 {System.out.println("表达式有误1,请检查");return null;} 200 if(a.charAt(i)=='$'||a.charAt(i)=='!') 201 if(helperStack.size()<1) 202 {System.out.println("表达式有误2,请检查");return null;} 203 //如果是操作符则,弹出相应的几个数,进行计算,并将结果压入 204 if(a.charAt(i)=='+'||a.charAt(i)=='-'||a.charAt(i)=='*'||a.charAt(i)=='/') 205 {expTree temp1=helperStack.pop();expTree temp2=helperStack.pop(); 206 expTree temp3=new expTree(a.charAt(i),temp2,temp1);helperStack.push(temp3); 207 } 208 209 if(a.charAt(i)=='!'||a.charAt(i)=='$') 210 {expTree temp1=helperStack.pop(); 211 expTree temp2=new expTree(a.charAt(i),temp1,null); 212 helperStack.push(temp2); 213 } 214 } 215 //读完之后若栈中还存在多余1个数则报错 216 if(helperStack.size()>1) 217 {System.out.println("表达式有误3,请检查");return null;} 218 return helperStack.pop(); 219 } 220 } 221 222 //构造表达式类 223 class expTree{ 224 char symbol; 225 expTree left; 226 expTree right; 227 expTree(){ 228 229 } 230 expTree(char temp){ 231 symbol=temp; 232 } 233 expTree(char temp,expTree leftTree,expTree rightTree){ 234 symbol=temp; 235 left=leftTree; 236 right=rightTree; 237 } 238 void printExpTree(expTree a,int width){ 239 //如果a不为空,则循环遍历,前序打印 240 if(a!=null){ 241 for(int i=0;i<width;i++) 242 System.out.print('\t'); 243 System.out.println(a.symbol); 244 printExpTree(a.left,width+1); 245 printExpTree(a.right,width+1); 246 247 } 248 } 249 }
5.演示结果
注意输入阶乘符号!时,不要输成了感叹号!