逆波兰式实现四则运算(加减乘除)
Posted ZK_小姜
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了逆波兰式实现四则运算(加减乘除)相关的知识,希望对你有一定的参考价值。
最近做一个项目需要做表达式的解析,初想不难,仔细研究之后,发现做细点可能会涉及到编译原理的词法解析和语法解析。但是如果只做简单的表达式计算,可以使用逆波兰式。
何为逆波兰式,可以看这个链接:https://baike.baidu.com/item/%E9%80%86%E6%B3%A2%E5%85%B0%E5%BC%8F/128437?fr=aladdin
简单研究了一下,下面是实现的简单四则运算:
public class RPNUtils
private static final Logger logger = LoggerFactory.getLogger(RPNUtils.class);
/**
* 运算符优先级map(数字越大,优先级越高)
*/
static Map<String,Integer> priorityMap = new HashMap<>(4);
static
priorityMap.put("+",1);
priorityMap.put("-",1);
priorityMap.put("*",2);
priorityMap.put("/",2);
priorityMap.put("(",0);
/**
* 计算表达式
* @param exp(中缀表达式)
* @return 计算结果
*/
public static Double calExp(String exp)
try
logger.info("传入计算表达式:", exp);
List<String> rpnList = transToRPN(exp);
logger.info("逆波兰式为:",JSON.toJSONString(rpnList));
Double result = cal(rpnList);
logger.info("计算结果为:",result);
return result;
catch (Exception e)
e.printStackTrace();
logger.error("表达式计算异常:"+exp,e);
return 0.0;
/**
* 转化成逆波兰式
* @param exp
* @return
*/
public static List<String> transToRPN(String exp)
//操作数栈
Stack<String> numStack = new Stack<>();
//运算符栈
Stack<String> operStack = new Stack<>();
//转化成字符数组
char[] expArray = exp.toCharArray();
StringBuffer sb = new StringBuffer();
List<String> numOperList = new ArrayList<>();
//分离运算符和操作数(在运算符和操作数之间添加空格符)
for (int i = 0; i < expArray.length; i++)
if (Character.isDigit(expArray[i]) || ".".equals(String.valueOf(expArray[i])))
sb.append(expArray[i]);
else
sb.append(" ").append(expArray[i]).append(" ");
//得到操作数和运算符的数组
String[] array = sb.toString().trim().split(" ");
//转化成list
numOperList = Arrays.asList(array);
for (int i = 0; i < numOperList.size(); i++)
String a = numOperList.get(i);
//过滤空字符串
if (a.equals(""))
continue;
if (Character.isDigit(a.charAt(0)))
//如果是操作数直接放入操作数栈
numStack.push(a);
else
if (operStack.isEmpty())
//如果是运算符,且运算符栈是空的,直接将运算符放入运算符栈
operStack.push(a);
continue;
if (a.equals("("))
//如果是左括号,直接放入运算符栈
operStack.push(a);
continue;
if (a.equals(")"))
//如果是右括号,则运算符栈依次出栈,并放入操作数栈,直到出栈运算符为左括号,并舍弃左括号
while (!operStack.peek().toString().equals("("))
numStack.push(operStack.pop());
operStack.pop();
continue;
//比较当前运算符和运算符栈顶的运算符
//如果栈顶运算符为左括号,则直接放入运算符栈
String topOper = operStack.peek();
if (topOper.equals("("))
operStack.push(a);
continue;
//比较当前运算符的优先级和运算符栈顶运算符的优先级,若大于(等于)栈顶运算符优先级,则直接放入运算符栈
if (priorityMap.get(a.toString()) > priorityMap.get(topOper.toString()))
operStack.push(a);
else
// 否则,运算符栈顶运算符出栈并放入操作数栈,直到运算符栈顶操作符优先级低于(不包含等于)该运算符优先级
do
numStack.push(operStack.pop());
while(!operStack.isEmpty() && priorityMap.get(operStack.peek()) >= priorityMap.get(a));
//最后当前运算符放入运算符栈
operStack.push(a);
//如果上面步骤走完了,运算符栈中还有运算符,则依次出栈,放入到操作数栈
while(!operStack.isEmpty())
numStack.push(operStack.pop());
return new ArrayList<>(numStack);
/**
* 根据逆波兰式计算表达式结果
* @param list
* @return
*/
public static Double cal(List<String> list)
System.out.println(JSON.toJSONString(list));
//计算栈
Stack<String> s = new Stack<>();
for (int i = 0; i < list.size(); i++)
String a = list.get(i);
if (Character.isDigit(a.charAt(0)))
//如果是数字,则直接入栈
s.push(a);
else
//如果是操作符,则计算栈栈顶两个元素依次出栈,进行计算,然后将计算结果入栈
if (a.equals("+"))
Double result = Double.valueOf(s.pop()) + Double.valueOf(s.pop());
s.push(String.valueOf(result));
if (a.equals("-"))
Double s1 = Double.valueOf(s.pop());
Double s2 = Double.valueOf(s.pop());
s.push(String.valueOf(s2 - s1));
if (a.equals("*"))
Double s1 = Double.valueOf(s.pop());
Double s2 = Double.valueOf(s.pop());
s.push(String.valueOf(s1*s2));
if (a.equals("/"))
Double s1 = Double.valueOf(s.pop());
Double s2 = Double.valueOf(s.pop());
s.push(String.valueOf(s2 / s1));
//计算完成,栈中元素便是计算结果
return Double.valueOf(s.pop());
public static void main(String[] args)
//String str = "(50-50*50/50)*5.1/5.1+50*20+15.5*(20-10)/5.0-(2-1)*(5/1.0)-2+(2*2)/ 4.0";
//String str = "10-1*5-2";
String str = "(10-3-2-3-1)*(20-10-5)*(3-2-2+2+4)";
System.out.println(calExp(str));
以上是关于逆波兰式实现四则运算(加减乘除)的主要内容,如果未能解决你的问题,请参考以下文章