逆波兰计算器就是使用逆波兰表达式实现的计算器
package com.data.algorithm.reversePolishNotation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Stack;
import java.util.regex.Pattern;
/**
* 用栈实现逆波兰计算器
* 主要步骤:
* 一,先把中缀表达式转为后缀表达式
* 1.1 初始化两个栈,运算符栈s1,存储中间结果栈s2
* 1.2 从左至右扫描中缀表达式
* 1.3 遇到操作数时把其压入s2
* 1.4 遇到运算符时,用其与s1栈顶运算符比较优先级
* 1.4.1 如果s1为空,或栈顶运算符为左括号“(”,则直接将此运算符入s1.
* 1.4.2 如果s1不为空,栈顶运算符也不为左括号“(”,则与s1栈顶运算符比较优先级,若比s1栈顶优先级高,也将其压入s1.
* 1.4.3 再否则,如果不比s1栈顶优先级高,就把s1栈顶运算符弹出并压入s2,然后再重复1.4与s1新栈顶比较
* 1.5 遇到括号时:
* 1.5.1 如果是“(”,则直接压入s1
* 1.5.2 如果是“)”,则依次弹出s1栈顶运算符压入s2,直到遇到“("为止,此时将这对括号丢弃
* 1.6 重复步骤1.2--1.5直至到表达式最右边
* 1.7 最后将s1剩余运算符依次弹出压入s2
* 1.8 依次弹出s2中元素,其逆序就是此中缀表达式对应的后缀表达式
* 二,按照逆波兰表单式的运算规则计算结果,以(3*4)*5-6为例,其后缀表达式为:3 4 + 5 * 6 -,计算步骤如下:
* 2.1 从左至右扫描,将3和4压入栈s3
* 2.2 遇到“+”,弹出3和4,计算3+4得7,将7再入栈s3
* 2.3 继续扫描,将5入栈
* 2.4 遇到“*”,弹出5和7,计算7*5,得35,将35入栈
* 2.5 继续扫描,将6入栈
* 2.6 最后是“-",计算35-6,得29,这就是最终结果
* @author wangjie
* @version V1.0
* @date 2020/3/1
*/
public class ReversePolishCalculator {
/**
* 判断是否是数字
*/
private static final Pattern isNumber = Pattern.compile("^[-\\+]?[.\\d]*$");
/**
*
* @param str
* @return
*/
public static Double calculator(String str){
Double doub = 0.0D;
try {
//1,校验参数
check(str);
//2,转为逆波兰表达式
List<String> list = toReversePolish(str);
//3,计算结果
doub = doCalc(list);
}catch(Exception e){
e.printStackTrace();
}
return doub;
}
/**
* 计算结果
* @param list
* @return
*/
private static Double doCalc(List<String> list) {
Double d = 0.0D;
if (list.size() == 1) {
//如果只剩一个数字
System.out.println(list);
d = Double.valueOf((String)list.get(0));
return d;
} else {
ArrayList<String> list1 = new ArrayList();
for(int i = 0; i < list.size(); ++i) {
list1.add((String)list.get(i));
if (isSymbol((String)list.get(i))) {
Double d1 = doTheMath((String)list.get(i - 2), (String)list.get(i - 1), (String)list.get(i));
list1.remove(i);
list1.remove(i - 1);
list1.set(i - 2, "" + d1);
list1.addAll(list.subList(i + 1, list.size()));
break;
}
}
doCalc(list1);
return d;
}
}
/**
* 根据字符集判断运算符,进而计算
* @param s1
* @param s2
* @param symbol
* @return
*/
private static Double doTheMath(String s1, String s2, String symbol) {
Double result;
switch(symbol.hashCode()) {
case 42:
if (symbol.equals("*")) {
result = Double.valueOf(s1) * Double.valueOf(s2);
return result;
}
break;
case 43:
if (symbol.equals("+")) {
result = Double.valueOf(s1) + Double.valueOf(s2);
return result;
}
break;
case 45:
if (symbol.equals("-")) {
result = Double.valueOf(s1) - Double.valueOf(s2);
return result;
}
break;
case 47:
if (symbol.equals("/")) {
result = Double.valueOf(s1) / Double.valueOf(s2);
return result;
}
}
result = null;
return result;
}
/**
* 参数校验
* @param str
*/
private static void check(String str){
if(null != str && !"".equals(str.trim())){
new RuntimeException("data is empty");
}
if(isNotNumber(String.valueOf(str.charAt(0)))){
throw new RuntimeException("data illeagle,start not with a number");
}
}
/**
* 判断字符串是否是数字
* @param str
* @return
*/
private static boolean isNotNumber(String str) {
return !isNumber.matcher(str).matches();
}
/**
* 中缀表达式转为后缀表达式
* @param str
* @return
*/
private static List<String> toReversePolish(String str){
List<String> data = Collections.synchronizedList(new ArrayList());
Stack<String> stack = new Stack();
str = replaceAllBlank(str);
int start = 0;
//遍历中缀表达式字符串
for(int i = 0; i < str.length(); ++i) {
String each;
//如果不是运算符
if (!isSymbol(String.valueOf(str.charAt(i)))) {
//如果是最后一个字符或下个字符是运算符
if (i == str.length() - 1 || isSymbol(String.valueOf(str.charAt(i + 1)))) {
each = start == 0 ? str.substring(start, i + 1) : str.substring(start + 1, i + 1);
if (isNotNumber(each)) {
throw new RuntimeException("data not match number");
}
data.add(each);
}
} else {
each = String.valueOf(str.charAt(i));
//如果是运算符“+-*/”或“(”
if (!stack.isEmpty() && !"(".equals(each) && (calcLevel(each) <= calcLevel((String)stack.peek()) || calcLevel(each) >= 2147483647)) {
if (!stack.isEmpty() && calcLevel(each) <= calcLevel((String)stack.peek())) {
while(!stack.isEmpty() && calcLevel(each) <= calcLevel((String)stack.peek()) && calcLevel((String)stack.peek()) != 2147483647) {
data.add((String)stack.pop());
}
stack.push(each);
} else if (")".equals(each)) {
while(!stack.isEmpty() && 2147483647 >= calcLevel((String)stack.peek())) {
if (2147483647 == calcLevel((String)stack.peek())) {
stack.pop();
break;
}
data.add((String)stack.pop());
}
}
} else {
stack.push(each);
}
start = i;
}
}
Collections.reverse(stack);
data.addAll(new ArrayList(stack));
System.out.println(data);
return data;
}
/**
* 判断运算符优先级
* @param s
* @return
*/
private static int calcLevel(String s) {
if (!"+".equals(s) && !"-".equals(s)) {
return !"*".equals(s) && !"/".equals(s) ? 2147483647 : 2;
} else {
return 1;
}
}
/**
* 判断是否运算符
* @param s
* @return
*/
private static boolean isSymbol(String s) {
return s.matches("\\+|-|\\*|/|\\(|\\)");
}
/**
* 去除所有空格
* @param s
* @return
*/
private static String replaceAllBlank(String s) {
return s.replaceAll("\\s+", "");
}
public static void main(String[] args) {
//定义一个中缀表达式字符串
String str = "22.9 + (2+9.8)*4/5 +8-9";
calculator(str);
}
}