前言
逆波兰表示法(Reverse Polish notation,RPN,或逆波兰记法),是一种是由波兰数学家扬·武卡谢维奇1920年引入的数学表达式方式,在逆波兰记法中,所有操作符置于操作数的后面,因此也被称为后缀表示法。逆波兰记法不需要括号来标识操作符的优先级,比如:3 – 4 + 5,转换为波兰表达式为:3 4 – 5 +
场景
以工单系统为例,比如发起了一个审批流程,通过规则引擎配置了如下规则:
$合同类型$ = ‘商务合同’ || ( $合同总金额$ > 1000000 && $合同总金额$ < 2000000 )
满足上面表达式会走审批1,否则走审批2逻辑。
对于上面的表达式,我们都知道首先应该先比较 || 两边的表达式,||右边的表达式因为是用括号连接,所以需要放在一起做&&比较,最后左右两边做 || 操作
但这是人思维方式,机器并不这样识别,机器可不知道哪个先比较,哪个后比较,所以可以将其转换为如下格式,逆波兰(后缀)表达式
合同类型、商务合同、=、合同总金额、1000000、>、合同总金额、2000000、
上面的表达式,机器就比较好识别了,一般利用栈来输出结果:
当为数值时压栈,当遇到表达式,从栈里面拿出两个数进行比较(运算)
算法流程介绍
转换为逆波兰表达式的基本思路是:
1、需要两个栈来存放“操作数”(如:合同类型)、运算符(如:||)
2、每个操作符都有自己的优先级
3、如果是操作数,则放入操作数栈
4、如果是运算符(不包括括号),则与运算符栈顶元素进行比较,如果优先级大于栈顶的元素,则直接入栈,如果小于,则将栈顶的元素取出,入操作数栈
5、如果运算符是“(”,则直接放入运算符栈顶,如果运算符是“)”,则从运算符依次取出元素放入操作数栈,直到遇到“(”
下面是我画的一个操作流程:
代码实现
public class ExpressionEvaluator {
private static String leftBraces = "(";
private static String rightBraces = ")";
private static Map operatorPriorityMap = new HashMap<>();
static {
operatorPriorityMap.put("(",-1);
operatorPriorityMap.put(")",-1);
operatorPriorityMap.put("||",1);
operatorPriorityMap.put("&&",1);
operatorPriorityMap.put("=",2);
operatorPriorityMap.put(">",2);
operatorPriorityMap.put("
// operatorPriorityMap.put("+",1);
// operatorPriorityMap.put("-",1);
// operatorPriorityMap.put("*",2);
// operatorPriorityMap.put("/",2);
}
/**
* 中缀表达式转化为后缀表达式
* @param expression
* @return
*/
public Queue parseExpression(String expression) {
String[] expressionArr = expression.split(" ");
Stack operatorStack = new Stack();
Queue operandQueue = new LinkedBlockingQueue<>();
for(int i=0;i
if (operatorPriorityMap.containsKey(expressionArr[i])) {
//操作符
if (expressionArr[i].equals(leftBraces)) {
//遇到左括号
operatorStack.add(expressionArr[i]);
}else if (expressionArr[i].equals(rightBraces)) {
//遇到右括号
appendLeftBracesOperator(operatorStack, operandQueue);
} else {
if (operatorStack.isEmpty()) {
operatorStack.add(expressionArr[i]);
continue;
}
String topOperate = operatorStack.peek();
if (comparePriority(expressionArr[i], topOperate) >= 0) {
//优先级大于栈顶的元素
operatorStack.add(expressionArr[i]);
} else {
//优先级小于栈顶的元素
appendLowPriority(operatorStack, operandQueue, expressionArr[i]);
}
}
} else {
//操作数
operandQueue.add(expressionArr[i]);
}
}
while (!operatorStack.isEmpty()) {
operandQueue.add(operatorStack.pop());
}
return operandQueue;
}
/**
* 将操作符栈低优先级的元素移到操作数队列中
* @param operatorStack
* @param operandStack
* @param operator
*/
private void appendLowPriority(Stack operatorStack, Queue operandStack, String operator) {
while (!operatorStack.isEmpty() && comparePriority(operator, operatorStack.peek()) < 0) {
String topOperate = operatorStack.pop();
operandStack.add(topOperate);
}
operatorStack.add(operator);
}
/**
* 将左括号之前的操作符元素移动到操作数栈中
* @param operatorStack
* @param operandStack
*/
private void appendLeftBracesOperator(Stack operatorStack, Queue operandStack) {
String topOperator = operatorStack.pop();
while (!topOperator.equals(leftBraces)) {
operandStack.add(topOperator);
topOperator = operatorStack.pop();
}
}
public static void main(String[] args) {
//String s = "1 + ( 2 + 3 * 4 ) * 2 / 4 - 5";
String s = "商务合同1 = 商务合同 || ( 1100000 > 1000000 && 1100000 < 2000000 )";
ExpressionEvaluator ee = new ExpressionEvaluator();
Queue queue = ee.parseExpression(s);
System.out.println(queue.toString());
}
}
逻辑运算
通过将表达式转换后,通过栈就可以实现对表达式的计算,具体思路:
1、遍历队列,如果是操作数,则入栈
2、如果是操作符,则从栈顶取出两个操作数进行运算操作
3、将最终结果返回
代码实现
/**
* 计算后缀表达式结果
* @param queue
* @return
*/
public boolean calcExpression(Queue queue) {
if(Objects.isNull(queue)) {
throw new RuntimeException("表达式为空");
}
String s = queue.poll();
Stack stack = new Stack();
while (Objects.nonNull(s)) {
if (operatorPriorityMap.containsKey(s)) {
Object o1 = stack.pop();
Object o2 = stack.pop();
if (">".equals(s)) {
stack.push(Double.parseDouble(o2.toString()) > Double.parseDouble(o1.toString()));
} else if ("
stack.push(Double.parseDouble(o2.toString()) < Double.parseDouble(o1.toString()));
} else if ("=".equals(s)) {
stack.push(o2.equals(o1));
} else if ("||".equals(s)) {
stack.push(Boolean.parseBoolean(o2.toString()) || Boolean.parseBoolean(o1.toString()));
} else if ("&&".equals(s)){
stack.push(Boolean.parseBoolean(o2.toString()) && Boolean.parseBoolean(o1.toString()));
}
} else {
stack.push(s);
}
s = queue.poll();
}
return Boolean.parseBoolean(stack.pop().toString());
}