逆波蘭式(Reverse Polish notation,RPN)是波蘭邏輯學家J・盧卡西維茲(J・ Lukasiewicz)於1929年首先提出的一種表達式的表示方法 ,也叫后綴表達式。
一般的表達式又稱中綴表達式,這種表達式的二元運算符放在兩個運算量之間。而逆波蘭表達式又稱后綴表達式,這種表達式把運算符放在運算量后面。
例如: a+b 的逆波蘭式表示為 ab+
它的優勢在於只用兩種簡單操作,入棧和出棧就可以搞定任何普通表達式的運算。其運算方式如下:
如果當前字符為變量或者為數字,則壓棧,如果是運算符,則將棧頂兩個元素彈出作相應運算,結果再入棧,最后當表達式掃描完后,棧里的就是結果。
import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
/**
* <h3>逆波蘭表達式示例</h3><br>
*/
public class ReversePolishNotation {
public static void main(String[] args) {
String expression = "A+B+((C+D*E)+F*G)+H/I-J";
List<String> infix = splitInfixExpression(expression);
List<String> rpn = toReversePolishNotation(expression);
String result = calculateReversePolishNotation(rpn);
System.out.println(expression);
System.out.println(infix);
System.out.println(rpn);
System.out.println(result);
}
/**
* 拆分中序表達式
* @param expression 中序表達式
* @return 拆分后的元素列表
*/
private static List<String> splitInfixExpression(String expression) {
List<String> elements = new ArrayList<>();
for (int i = 0, j = 0, k = expression.length() - 1; i <= k; i++) {
char c = expression.charAt(i);
// 運算符
if (c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')') {
if (i != j) {
elements.add(expression.substring(j, i));
}
elements.add(String.valueOf(c));
j = i + 1;
} else if (i == k) {
elements.add(expression.substring(j));
}
}
return elements;
}
/**
* 檢查算術表達術括號是否匹配, 語法是否正確
* @param expression 算術表達術
* @return 語法是否正確
*/
public static boolean isMatch(String expression) {
// 括號符號棧
Deque<Character> stack = new LinkedList<>();
// 遍歷字符串
for (int i = 0; i < expression.length(); i++) {
char c = expression.charAt(i);
// 括號開始
if (c == '(') {
stack.push(c);
} else if (c == ')') {
// 括號結束 ,且棧為空則返回 false
if (stack.isEmpty()) {
return false;
}
// 且棧不為空彈出棧頂
else {
stack.pop();
}
}
}
// 棧為空(所有括號已經閉合)則表達式正確
return stack.isEmpty();
}
/**
* 解析表達式,獲取逆波蘭式
* @param expression 表達式
* @return 逆波蘭式隊列
*/
public static List<String> toReversePolishNotation(String expression) {
if (!isMatch(expression)) {
throw new RuntimeException("Expression parentheses do not match!");
}
// 逆波蘭表達式棧
List<String> result = new ArrayList<>();
// 運算符棧(棧底到棧頂遞增。棧頂必須大於下面的)
Deque<String> stack = new LinkedList<>();
// 遍歷表達式元素
for (String element : splitInfixExpression(expression)) {
// 運算符
if (isOperator(element)) {
// 檢測棧頂與當前優先級關系,如果棧頂大於等於當前則出棧並輸出;直至棧頂小於當前,並將當前操作符入棧
while (!stack.isEmpty() && getPriority(stack.peek()) > getPriority(element)) {
result.add(stack.pop());
}
stack.push(element);
}
// 左括號
else if ("(".equals(element)) {
stack.push(element);
}
// 右括號
else if (")".equals(element)) {
// 只要操作符不為左括號則一直輸出
for (String top = stack.pop(); !"(".equals(top); top = stack.pop()) {
result.add(top);
}
}
// 運算量
else {
result.add(element);
}
}
// 依次彈出棧中剩下的操作符,並輸出
while (!stack.isEmpty()) {
String top = stack.pop();
if (!"(".equals(top)) {
result.add(top);
}
}
return result;
}
/**
* 計算逆波蘭式
* @param rpn 逆波蘭式序列
* @return 逆波蘭式結果
*/
public static String calculateReversePolishNotation(List<String> rpn) {
Deque<String> stack = new LinkedList<>();
for (String element : rpn) {
if (isOperator(element)) {
String value2 = stack.pop();
String value1 = stack.pop();
stack.push("(" + value1 + element + value2 + ")");
} else {
// 操作量入棧
stack.push(element);
}
}
return stack.pop();
}
/**
* 判斷是否為操作符 + - * /
* @param value 字符
* @return 是否操作符
*/
private static boolean isOperator(String value) {
return "+".equals(value) || "-".equals(value) || "*".equals(value) || "/".equals(value);
}
/**
* 獲得運算符優先級
* @param operator 運算符
* @return 運算符優先級
*/
private static int getPriority(String operator) {
switch (operator) {
case "*":
case "/":
return 2;
case "+":
case "-":
return 1;
case "(":
return 0;
default:
throw new RuntimeException("Unsupported Operator [" + operator + "] !");
}
}
}