逆波蘭表達式
又稱后綴表達式,將運算符寫在操作數之后,便於計算機計算
- 將中綴表達式轉換成后綴表達式的8個步驟:
-
初始化兩個棧,運算符棧s1和存儲中間結果的棧s2
-
從左至右掃描中綴表達式
-
遇到操作數時,將其壓入s2
-
遇到運算符時,將其與s1棧頂運算符的優先級進行比較:
- 如果s1為空,或棧頂運算符為左括號"(",則直接將此運算符入棧
- 否則,若優先級比棧頂運算符的高,也將運算符壓入s1
- 否則,將s1棧頂的運算符彈出並壓入到s2中,再次轉到(4.1)與s1中的棧頂運算符相比較
-
遇到括號時:
- 如果是左括號"(",則直接壓入s1
- 如果是右括號")",則依次彈出s1棧頂的運算符,並壓入s2,直到遇到左括號為止,此時將這一對括號丟棄
-
重復步驟2至5,知道表達式最右邊
-
將s1中剩余的運算符以此彈出並壓入s2
-
依次彈出s2中的元素並輸入,結果的逆序即為中綴表達式對應的后綴表達式。
- 1+(2+3)*4 -5 的轉換步驟
代碼實現
/**
* 轉換成后綴表達式List
*
* @param ls 中綴表達式List
* @return
*/
public static List<String> parseSuffixExpression(List<String> ls) {
Stack<String> s1 = new Stack<>();//運算符棧
Queue<String> s2 = new ArrayDeque<>();//臨時隊列
for (String item : ls) {
if (item.matches("\\d+")) {
//3.如果是數字,直接入隊s2
s2.offer(item);
} else if (isOper(item)) {
//4.如果是運算符
while (true) {
if (s1.isEmpty() || "(".equals(s1.peek())) {
//4.1如果運算符棧s1是空的,或棧頂是左括號,則直接入棧
s1.push(item);
break;
} else if (priority(item) > priority(s1.peek())) {
//4.2否則,若運算符優先級比棧頂的高,也入棧
s1.push(item);
break;
} else {
//4.3 否則,直接把棧頂的元素入隊,繼續轉到4.1與新的棧頂元素比較
s2.offer(s1.pop());
}
}
} else if ("(".equals(item)) {
//5.1如果遇到左括號( ,直接壓入s1
s1.push(item);
} else if (")".equals(item)) {
//5.2如果是右括號
while (true) {
String top = s1.pop();//彈出棧頂元素
if ("(".equals(top)) {
//碰到左括號,丟棄這對括號
break;
} else {
//否則入隊
s2.offer(top);
}
}
} else {
throw new RuntimeException("無法識別的字符" + item + ",表達式不正確");
}
}
while (!s1.isEmpty()) {
//7.將s1剩余的表達式依次彈出並入隊
s2.offer(s1.pop());
}
List<String> res = new ArrayList<>();
while (!s2.isEmpty()) {
res.add(s2.poll());
}
return res;
}
后綴表達式的計算規則:從左到右遍歷表達式的每個數字和符號,遇到是數字就進棧,遇到是符號,就將處於棧頂兩個數字出棧,進行運算,運算結果進棧,一直到最終獲得結果。
/**
* 計算后綴表達式List,返回結果
*
* @param ls
* @return
*/
public static int calculate(List<String> ls) {
Stack<String> stack = new Stack<>();
for (String item : ls) {
if (item.matches("\\d+")) {
//如果是數字則入棧
stack.push(item);
} else {
//如果不是數字,則彈出棧頂和次棧頂元素進行計算
int num2 = Integer.parseInt(stack.pop());
int num1 = Integer.parseInt(stack.pop());
int res = 0;
if ("+".equals(item)) {
res = num1 + num2;
} else if ("-".equals(item)) {
res = num1 - num2;
} else if ("*".equals(item)) {
res = num1 * num2;
} else if ("/".equals(item)) {
res = num1 / num2;
} else {
throw new RuntimeException("運算符有誤");
}
//計算過的結果再次入棧
stack.push("" + res);
}
}
//棧中的結果就是表達是的結果,直接返回
return Integer.parseInt(stack.pop());
}
簡單轉換(括號法)
1.按照運算符的優先級對所有的運算單位加括號~
2.把運算符號移動到對應的括號后面
3.去除所有括號
例:
- 1+2
- (1+2)
- (1 2)+
- 1 2 +
- 1+2×3
- (1+(2 × 3))
- (1+(2 3)×)
- (1 (2 3 ×) ) +
- 1 2 3 × +
- (2-1)×3+15÷(1+4)
- (((2 - 1) × 3) + (15 ÷ (1+4)))
- (((2 1) - × 3) + (15 ÷ (1 4)+))
- (((2 1) - 3) × + (15 (1 4)+) ÷ )
- (((2 1) - 3) × (15 (1 4)+) ÷ ) +
- 2 1 - 3 × 15 1 4 + ÷ +
//完整java代碼
import java.util.*;
/**
* 逆波蘭表達式(后綴表達式)
* 支持整數、浮點 + - * / ( )運算符,可自動過濾非法字符
*/
public class PolandNotation {
public static void main(String[] args) {
String expression = "(2.1-1)* 3.1+ 15/(2+4)";
System.out.println("中綴表達式:" + expression);
List<String> list = toInfixExpressionList(expression);
System.out.println("中綴表達式List:" + list);
List<String> suffixExpression = parseSuffixExpression(list);
System.out.println("后綴表達式List:" + suffixExpression);
float calculate = calculate(suffixExpression);
System.out.println("計算結果:" + calculate);
}
/**
* 轉換成后綴表達式List
*
* @param ls 中綴表達式List
* @return
*/
public static List<String> parseSuffixExpression(List<String> ls) {
Stack<String> s1 = new Stack<>();//運算符棧
Queue<String> s2 = new ArrayDeque<>();//臨時隊列
for (String item : ls) {
if (item.matches(REGEX_FLOAT)) {
//3.如果是數字,直接入隊s2
s2.offer(item);
} else if (isOper(item)) {
//4.如果是運算符
while (true) {
if (s1.isEmpty() || "(".equals(s1.peek())) {
//4.1如果運算符棧s1是空的,或棧頂是左括號,則直接入棧
s1.push(item);
break;
} else if (priority(item) > priority(s1.peek())) {
//4.2否則,若運算符優先級比棧頂的高,也入棧
s1.push(item);
break;
} else {
//4.3 否則,直接把棧頂的元素入隊,繼續轉到4.1與新的棧頂元素比較
s2.offer(s1.pop());
}
}
} else if ("(".equals(item)) {
//5.1如果遇到左括號( ,直接壓入s1
s1.push(item);
} else if (")".equals(item)) {
//5.2如果是右括號
while (true) {
String top = s1.pop();//彈出棧頂元素
if ("(".equals(top)) {
//碰到左括號,丟棄這對括號
break;
} else {
//否則入隊
s2.offer(top);
}
}
} else {
throw new RuntimeException("無法識別的字符" + item + ",表達式不正確");
}
}
while (!s1.isEmpty()) {
//7.將s1剩余的表達式依次彈出並入隊
s2.offer(s1.pop());
}
List<String> res = new ArrayList<>();
while (!s2.isEmpty()) {
res.add(s2.poll());
}
return res;
}
/**
* 是否操作符
*
* @param val 元素
* @return
*/
public static boolean isOper(String val) {
return "+".equals(val) || "-".equals(val) || "*".equals(val) || "/".equals(val);
}
/**
* 將中綴表達式轉換成對應的List
*
* @param s 中綴表達式字符串
* @return
*/
public static List<String> toInfixExpressionList(String s) {
List<String> ls = new ArrayList<>();
int i = 0;//指針,用於遍歷中綴表達式字符串
String str;//對多位數的拼接工作
char c;//每遍歷到一個字符,放入c
do {
c = s.charAt(i);//獲取元素
if (c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')') {
//如果元素是運算符直接添加到List
ls.add(c + "");
i++;
} else if (Character.isDigit(c) || c == '.') {
//如果元素是數字和小數點
str = "";
while (i < s.length() && (Character.isDigit(s.charAt(i)) || s.charAt(i) == '.')) {//直至下個不是小數點或數字的位置
str += "" + s.charAt(i++);
}
ls.add(str);
} else {
i++;
}
} while (i < s.length());
return ls;
}
/**
* 返回操作符優先級
*
* @param operString 操作符
* @return
*/
public static int priority(String operString) {
if ("*".equals(operString) || "/".equals(operString)) {
return 1;
} else if ("+".equals(operString) || "-".equals(operString)) {
return 0;
} else {
return -1;
}
}
/**
* 數字正則
*/
private final static String REGEX_FLOAT = "\\d+||\\d*\\.\\d+||\\d*\\.?\\d+?e[+-]\\d*\\.?\\d+?||e[+-]\\d*\\.?\\d+?";
/**
* 計算后綴表達式List,返回結果
*
* @param ls
* @return
*/
public static float calculate(List<String> ls) {
Stack<String> stack = new Stack<>();
for (String item : ls) {
if (item.matches(REGEX_FLOAT)) {
stack.push(item);//如果是數字則入棧
} else {
//如果不是數字,則彈出棧頂和次棧頂元素進行計算
Float num2 = Float.parseFloat(stack.pop());
Float num1 = Float.parseFloat(stack.pop());
Float res = 0f;
if ("+".equals(item)) {
res = num1 + num2;
} else if ("-".equals(item)) {
res = num1 - num2;
} else if ("*".equals(item)) {
res = num1 * num2;
} else if ("/".equals(item)) {
res = num1 / num2;
} else {
throw new RuntimeException("運算符有誤");
}
//計算過的結果再次入棧
stack.push("" + res);
}
}
//棧中的結果就是表達是的結果,直接返回
return Float.parseFloat(stack.pop());
}
}