模拟一个简单的逆波兰计算器(后缀表达式计算)
可以简单实现加、 减、乘、除、带括号和多位数的运算,不支持小数点(因为懒)。有详细的代码注释,有些地方可能没有注释到,请见谅。
package com.java.stack;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/**
* 逆波兰计算器
*/
public class StackCal {
public static void main(String[] args) {
doNiBolan("1 + ( ( 2 + 3 ) * 4) - 5");
}
/**
* 将普通的中缀表达式转化成后缀表达式(逆波兰表达式)
* @param expression 传入的中缀表达式
* @return 转换后的逆波兰表达式
*/
private static String toNiBolan(String expression) {
//转换成标准的表达式
List<String> stanExp = toStandardExpression(expression);
//开始真正转换
//1 + ( ( 2 + 3 ) * 4) - 5 -> 1 2 3 + 4 * + 5 -
//创建操作符栈和数栈
Stack<String> opeStack = new Stack<>();
Stack<String> numStack = new Stack<>();
//遍历标准表达式集合
for (String item : stanExp) {
if(item.matches("\\d+")){
//是数字,直接入数栈
numStack.push(item);
}else if(opeStack.empty() || item.equals("(")){
//栈是空的,或者匹配的是左括号,就直接入栈
opeStack.push(item);
}else if(item.equals(")")){
//匹配到右括号,从操作符栈弹出所有操作符到数栈,直到匹配到第一个左括号
while (!opeStack.peek().equals("(")){
numStack.push(opeStack.pop());
}
//最后把操作符栈的左括号移除,这一步非常重要
opeStack.pop();
}else if(Priority.getValue(item) > Priority.getValue(opeStack.peek())){
//进来的运算符优先级比栈顶的高,且不是右括号,也直接入操作符栈
opeStack.push(item);
}else {
//如不高,从操作符栈弹出一个如数栈,再将新来的item如操作符栈
numStack.push(opeStack.pop());
opeStack.push(item);
}
}
//把最后剩在操作符栈的元素如数栈
for (String s : opeStack) {
numStack.push(s);
}
//对最终要返回的结果进行处理,比如去除两边的"[","]",和","
String s = numStack.toString();
String retStr = s.replace("[","")
.replace(",","")
.replace("]","");
System.out.println("转换成的逆波兰表达式是:" + retStr);
return retStr;
}
/**
* 将表达式转换成List集合,并且支持多位数,支持过滤空格,换行符,tab
* @param expression
* @return 处理之后的list集合
*/
public static List<String> toStandardExpression(String expression){
//去除expression中的多余的空格,换行符,等等
String newExp = expression
.replace(" ", "")
.replace("\n", "")
.replace("\t","")
.replace("\r","")
.trim();
List<String> retExp = new ArrayList<>();
int index = 0;
for(int i = 0; i < newExp.length();){
char c = newExp.charAt(i);
if(c >= 48 && c <= 57){
//说明该位置是一个数字
String str = "";
str += c;
i++;
while( i < newExp.length() && newExp.charAt(i) >= 48 && newExp.charAt(i) <= 57){
str += newExp.charAt(i);
i++;
}
retExp.add(str);
}else {
//非数字进行操作
retExp.add(""+c);
i++;
}
}
System.out.println("标准表达式数组:" + retExp.toString());
return retExp;
}
/**
* 此处是计算逆波兰表达式的方法,假设传入的表达式是标准写法:都有空格把每个元素隔开
* @param expression 传入的逆波兰表达式
*/
public static void doNiBolan(String expression){
String newExp = toNiBolan(expression);
String[] splitString = newExp.split(" ");
Stack<String> numStack = new Stack<>();
for (int i = 0; i < splitString.length; i++) {
String s = splitString[i];
//如果是一个数字
if(!isOperator(s)){
numStack.push(s);
}else {
//是一个运算符
String num2 = numStack.pop();
String num1 = numStack.pop();
String res = calc(num1, num2, s);
numStack.push(res);
}
}
System.out.println("使用逆波兰计算的结果是:" + numStack.pop());
}
/**
* 判断是否是 + - * / 四种运算符
* 虽然要考虑括号情况,但是逆波兰表达式并没有括号,所以不用判断
* @param s
* @return
*/
public static boolean isOperator(String s){
boolean flag = false;
if(s.equals("+") || s.equals("-") || s.equals("/") || s.equals("*")){
flag = true;
}
return flag;
}
/**
* 计算加减乘除的方法
* @param num1 第一个数
* @param num2 第二个数
* @param operator 运算符
* @return
*/
public static String calc(String num1, String num2, String operator){
if(operator.equals("+")){
Integer i = Integer.parseInt(num1) + Integer.parseInt(num2);
return i.toString();
}else if(operator.equals("-")){
Integer i = Integer.parseInt(num1) - Integer.parseInt(num2);
return i.toString();
}else if(operator.equals("*")){
Integer i = Integer.parseInt(num1) * Integer.parseInt(num2);
return i.toString();
}else if(operator.equals("/")){
Integer i = Integer.parseInt(num1) / Integer.parseInt(num2);
return i.toString();
}
return null;
}
}
/**
* 一个计算优先级的类
*/
class Priority{
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;
switch (operation) {
case "+":
result = ADD;
break;
case "-":
result = SUB;
break;
case "*":
result = MUL;
break;
case "/":
result = DIV;
break;
default:
break;
}
return result;
}
}
最后运行的结果