中綴表示式轉字尾表示式
方式一
步驟
1️⃣ 如果遇到運算元,我們就直接將其輸出。
2️⃣ 如果遇到操作符,則我們將其放入到棧中,遇到左括號時我們也將其放入棧中。
3️⃣ 如果遇到一個右括號,則將棧元素彈出,將彈出的操作符輸出直到遇到左括號為止。注意,左括號只彈出並不輸出。
4️⃣ 如果遇到任何其他的操作符,如(“+”, “*”,“(”)等,從棧中彈出元素直到遇到發現更低優先順序的元素(或者棧為空)(或發現最近的左括號)為止。彈出完這些元素後,才將遇到的操作符壓入到棧中。有一點需要注意,只有在遇到" ) "的情況下我們才彈出" ( ",其他情況我們都不會彈出" ( "。
5️⃣ 如果我們讀到了輸入的末尾,則將棧中所有元素依次彈出。
程式碼實現
package demo03;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
public class InfixToPostfixExpression {
private char[] expression;
private Deque<Character> stack;
private ArrayList<Character> list; // 儲存答案
public InfixToPostfixExpression(char[] ex) {
this.expression = ex;
stack = new ArrayDeque<>();
list = new ArrayList<>();
}
public ArrayList process() {
for (char c : expression) {
if (isOperand(c)) { //如果是運算元,入集合
list.add(c);
}
else if (isBracket(c)) { // 如果是括號
if (isLeftBracket(c)) { // 如果是左括號,入棧
stack.addLast(c);
} else { // 如果是右括號
while (!stack.isEmpty() && stack.peekLast() != '(') { // 一直彈出棧中元素知道遇到左括號(左括號也要但還沒彈出)
list.add(stack.removeLast());
}
if (!stack.isEmpty()) stack.removeLast(); //移除棧中匹配的左括號
}
} else if (isOperator(c)) { // 如果 c 是操作符
if (stack.isEmpty()) { // 如果棧空,則入棧
stack.addLast(c);
}
else {
// 計算優先值(0:相等,-1:c優先值低於棧頂,1:c優先值大於棧頂)
int priority = priority(c) - priority(stack.peekLast());
if (priority < 0) { //遇到優先度低的,一直彈出來(直到遇到左括號)
while (!stack.isEmpty() && !isLeftBracket(stack.peekLast())) {
list.add(stack.removeLast());
}
}
stack.addLast(c); // 最後 c 入棧
}
}
}
while (!stack.isEmpty()) { // 棧中剩餘元素都彈出併入集合
list.add(stack.removeLast());
}
return list;
}
// 判斷是否是運算元
private boolean isOperand(char c) {
return !(isOperator(c) || isBracket(c));
}
// 計算優先值
private int priority(char c) {
if (c == '+' || c == '-') return 0;
else return 1; //if (c == '*' || c == '/')
}
// 判斷是否是左括號
private boolean isLeftBracket(char c) {
return c == '(';
}
// 判斷是否是右括號
private boolean isRightBracket(char c) {
return c == ')';
}
// 判斷是否是操作符
public boolean isOperator(char c) {
return c == '+' || c == '-' || c == '*' || c == '/';
}
// 判斷是否是括號
public boolean isBracket(char c) {
return c == '(' || c == ')';
}
}
測試
public static void main(String[] args) {
String ex = "a + b * c + (d * e + f)*g";
ex = ex.trim().replace(" ", "");
char[] e = ex.toCharArray();
InfixToPostfixExpression expression = new InfixToPostfixExpression(e);
ArrayList process = expression.process();
System.out.println(process.toString());
// 輸出:[a, b, c, *, +, d, e, *, f, +, g, *, +]
}
方式二
1️⃣ 先按照運算子的優先順序對中綴表示式加括號,變成((a + (b * c)) + (((d * e) + f) * g ))
2️⃣ 將運算子移到括號的後面,變成((a(bc) * )+(((de) * f) + g) * ) +
3️⃣ 去掉括號,得到 abc *+ de * f + g *+
字尾表示式的計算
字尾表示式也叫逆波蘭表示式。
逆波蘭表示式嚴格遵循「從左到右」的運算。計算逆波蘭表示式的值時,使用一個棧儲存運算元,從左到右遍歷逆波蘭表示式,進行如下操作:
如果遇到運算元,則將運算元入棧;
如果遇到運算子,則將兩個運算元出棧,其中先出棧的是右運算元,後出棧的是左運算元,使用運算子對兩個運算元進行運算,將運算得到的新運算元入棧。
整個逆波蘭表示式遍歷完畢之後,棧內只有一個元素,該元素即為逆波蘭表示式的值。
程式碼
class Solution {
public int evalRPN(String[] tokens) {
Deque<Integer> stack = new LinkedList<>();
int n = tokens.length;
for(int i = 0;i<n;i++){
if(isNum(tokens[i])){
stack.push(Integer.parseInt(tokens[i]));
}else{
int b = stack.pop();
int a = stack.pop();
switch(tokens[i]){
case "+":{
stack.push(a+b);
break;
}
case "-":{
stack.push(a-b);
break;
}
case "*":{
stack.push(a*b);
break;
}
case "/":{
stack.push(a/b);
break;
}
}
}
}
return stack.pop();
}
public boolean isNum(String s){
return !(("+".equals(s))||("-".equals(s))||("*".equals(s))||("/".equals(s)));
}
}
輸入:tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
輸出:22
解釋:
該算式轉化為常見的中綴算術表示式為:
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22