这几天复习数据结构,
复习到栈那一章,其中有一个使用栈来解析算术表达式的实例,但是只能解析由一位数字组成的表达式, 如过是 2*5+18 这样出现两位及以上的操作数就不能解析了。
于是重写了一个比较完善的算术表达式解析程序:
以下是完整的代码 带注释应该挺详细的了:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Stack;
/**
*
* @author Mr.Free
* Compute Arithmetic Expression using stack ds.
* input arithmetic expression like: 3*(15+9)/9, compute the result
* using stack
* Steps: 1. tanslate an infix expression into a postfix expression
* 2. parsing it.
*/
public class StackTest {
public static Stack<String> operator = new Stack<String>(); //操作符号栈,用来存放操作符号,将会在中缀转后缀表达式程序中使用
public static Stack<String> operation = new Stack<String>();//
/**
* 这段是将中缀表达式分解成字符串数组,这样就能操作多位操作数了
*
*/
public static String[] splitIntoArray(String expStr) {
char c;
ArrayList<String> opList = new ArrayList<String>();
String temp;
Stack<String> s = new Stack<String>();
for(int i = 0; i< expStr.length(); i++) {
temp = "";
c = expStr.charAt(i);
if(c <= '9' && c >= '0') {
s.push(String.valueOf(c));
} else {
while(!s.isEmpty())
{
temp += String.valueOf(s.pop());
}
//add operand,因为是栈,弹出多位操作数后需要将它倒序输出
if(!temp.equals("")) opList.add(new StringBuffer(temp).reverse().toString());
opList.add(String.valueOf(c)); // add operator
}
}
temp ="";
//add the remaining operator
while(!s.isEmpty()) {
temp += String.valueOf(s.pop());
}
if(temp!="") opList.add(temp);
String []str;
str = opList.toArray(new String[opList.size()]);
return str;
}
/**
* 中缀表达式转后缀表达式,很好的利用了栈的先进后出的特性
* 返回后缀表达式
*/
public static String[] infix2Postfix(String []str) {
// String []postfix = new String[str.length];
ArrayList<String> postList = new ArrayList<String>();
for(int i = 0; i<str.length; i++) {
if(!str[i].matches("[+\\-\\(\\)\\*/]")) {
postList.add(str[i]);
} else {
//operator length is just one, so can use switch statement
char oper = str[i].charAt(0);
String rs;
switch(oper) {
case '+':
case '-':
rs = getOper(String.valueOf(oper), 1); // 1 use to presents the precedence,1代表优先级,+- 是1
if(rs != "") postList.add(rs); ;
break;
case '*':
case '/':
rs = getOper(String.valueOf(oper), 2); //2 * /乘除是2
if(rs != "") postList.add(rs) ;
break;
case '(': //括号是最高优先级,不需要放getOper里面判断了
operator.push("(");
break;
case ')':
postList.add(operator.pop());
operator.pop(); // delete "("
break;
}
}
}
while(!operator.isEmpty()) postList.add(operator.pop()); //return the remaining operator
String []postfix;
postfix = postList.toArray(new String[postList.size()]);
return postfix;
}
/**
* 用于在中缀转后缀过程中取+-* /符号
* 取操作符号时总是比较新的操作符和旧操作符,新操作符优先级别小于等于旧操作符就返回旧操作符,压入新操作符,否则
* 压入新操作符返回nothing
* 例:5+6+7 按人脑计算步骤我们总是看到5+6后的+号 我们才开始计算5+6。 此处同理
*/
public static String getOper(String oper, int precNew) {
String oldTop; // old operator
if(operator.isEmpty()) {
operator.push(oper);
return ""; //return nothing
} else {
oldTop = operator.peek();
int precOld = 0;
if(oldTop.matches("\\(")) {
operator.push(oper);
return "";
} else {
if (oldTop.matches("[+-]")) {
precOld = 1;
} else {
precOld = 2;
}
if(precNew <= precOld) {
String operNew = operator.pop();
operator.push(oper);
return operNew; // return operator
} else {
operator.push(oper); // stor new operator
return ""; // retunr nothing
}
}
}
}
/**
* 计算后缀表达式,这步挺简单的,后缀表达式可以从左到右按顺序计算(这就是为什么我们要花力气将表达式转换为后缀表达式)
* 遇到操作符便取前两个操作数计算。操作数存放在operation栈里。。其实应该叫operand 栈贴切一些。。懒得改了将就吧。
*
*/
public static double parsePostfix(String []str) {
double result = 0;
char c;
for(int i = 0; i< str.length; i++) {
if(!str[i].matches("[+\\-\\*/]")) {
operation.push(str[i]); // push operands
} else {
c = str[i].charAt(0);
switch(c) {
case '+':
result = Double.parseDouble(operation.pop())
+ Double.parseDouble(operation.pop());
operation.push(String.valueOf(result));
break;
case '-'://注意减法后取出的是被减数,先出栈的是减数,同理在除法部分也有体现,先出栈的是除数
//后出栈的是被除数。
result = -Double.parseDouble(operation.pop())
+ Double.parseDouble(operation.pop());
operation.push(String.valueOf(result));
break;
case '*':
result = Double.parseDouble(operation.pop())
* Double.parseDouble(operation.pop());
operation.push(String.valueOf(result));
break;
case '/':
result = 1 / Double.parseDouble(operation.pop())
* Double.parseDouble(operation.pop());
operation.push(String.valueOf(result));
break;
}
}
}
return result;
}
//测试程序。。
public static void main (String []args) {
String expression = getInput();
String []splitOp = splitIntoArray(expression);
String []rs = infix2Postfix(splitOp);
for(String r: rs) {
System.out.print(r);
}
System.out.println("\n" + expression +"=" + parsePostfix(rs));
}
private static String getInput() {
// TODO Auto-generated method stub
System.out.println("Please input an expression:");
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
try {
return bf.readLine();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
bf.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
}