版權聲明:本文源自簡書tianma,轉載請務必注明出處: http://www.lxweimin.com/p/a0d4764eba18
中綴表達式: 是一個通用的算術或邏輯公式表示方法, 操作符是以中綴形式處于的中間(例:3 + 4),中綴表達式是人們常用的算術表示方法,但是不易被計算機所解析。
后綴表達式:是一個通用的算術或邏輯公式表示方法, 操作符是后綴形式處于操作數的后面(例:3 4 +),后綴表達式雖然不是人們所習慣的運算表示方法,但是易被計算機解析。
例如:對于中綴表達式 9+(3-1)*2+10/2 , 其后綴表達式是 9 3 1 - 3 * + 10 2 / + , 那么為了方便計算機解析計算,我們需要將中綴表達式轉換成后綴表達式,然后再對后綴表達式進行解析。
1. 中綴表達式轉后綴表達式:
- 當讀到一個操作數時,立即將它放到輸出中。讀到的是操作符則需要接著判斷是否該入棧。讀到的是左圓括號則入棧。
- 在讀到操作符時,如果棧為空或者棧頂操作符為(,則入棧。如果棧頂操作符不為(,且此操作符優先級小于或等于此時棧頂操作符,則將棧中元素彈出直至 ①遇到左括號 或者 ②棧頂元素為更低優先級 或者 ③棧為空為止,并將當前操作符入棧;否則當前操作符繼續入棧。操作符中,+-優先級低,*/優先級高。
- 如果遇到一個右括號,那么就將棧中元素彈出并輸出直至遇到左括號為止。但是這個左括號只被彈出,并不輸出。
- 如果讀到輸入的末尾,若棧不為空則將棧元素彈出直到該棧變成空棧,并將彈出的符號寫到輸出中。
"9+(3-1)2+10/2" 轉換過程:*
操作過程 | 棧中元素 | 輸出 |
---|---|---|
讀入 9,輸出 | 9 | |
讀入 +,棧為空,規則2,入棧 | + | 9 |
讀入 ( ,左括號,規則1,入棧 | + ( | 9 |
讀入 3,輸出 | + ( | 9 3 |
讀入 -,棧頂為(,規則2,入棧 | + ( - | 9 3 |
讀入 1,輸出 | + ( - | 9 3 1 |
讀入 ) ,右括號,規則3,出棧并輸出 | + | 9 3 1 - |
讀入 *,*優先級高于棧頂+,規則,2,入棧 | + * | 9 3 1 - |
讀入 3,輸出 | + * | 9 3 1 - 3 |
讀入 +,+優先級低于棧頂*,規則2,棧中元素出棧,當前操作符入棧 | + | 9 3 1 - 3 * + |
讀入 10, 輸出 | + | 9 3 1 - 3 * + 10 |
讀入 / , /優先級高于+,入棧 | + / | 9 3 1 - 3 * + 10 |
讀入 2, 輸出 | + / | 9 3 1 - 3 * + 10 |
讀至末尾,規則4,棧不為空,棧中元素出棧并輸出 | 9 3 1 - 3 * + 10 / + |
2. 后綴表達式計算最終結果:
- 從左到右遍歷表達式的每個數字和符號,遇到是數字則進棧,遇到是運算符則將棧頂兩個元素出棧,進行運算并將運算結果進棧;
- 遍歷完后綴表達式,此時棧中剩余的數字就是運算結果。
"9 3 1 - 3 * + 10 2 / +" 計算過程:
操作過程 | 棧中元素 |
---|---|
讀入 9,入棧 | 9 |
讀入 3,入棧 | 9 3 |
讀入 1,入棧 | 9 3 1 |
讀入 -,運算并將結果入棧 | 9 2 |
讀入 3,入棧 | 9 2 3 |
讀入 *,運算并將結果入棧 | 9 6 |
讀入 +,運算并將結果入棧 | 15 |
讀入 10,入棧 | 15 10 |
讀入 2,入棧 | 15 10 2 |
讀入 /,運算并將結果入棧 | 15 5 |
讀入 +,運算并將結果入棧 | 20 |
讀入完畢,棧中元素即為結果 | 20 |
簡單中綴表達式計算的java實現:
public class SimpleCalcutor {
private class Item {
private String value;
private Integer number;
public Item(String value) {
this.value = value;
try {
number = Integer.parseInt(value);
} catch (Exception ignore) {
}
}
public boolean isNumber() {
return number != null;
}
public int getNumber() {
if (isNumber())
return number;
throw new NumberFormatException();
}
public boolean isAdd() {
return "+".equals(value);
}
public boolean isSub() {
return "-".equals(value);
}
public boolean isMul() {
return "*".equals(value);
}
public boolean isDiv() {
return "/".equals(value);
}
public boolean isLeftBracket() {
return "(".equals(value);
}
public boolean isRightBracket() {
return ")".equals(value);
}
public int getPriority() {
if (isAdd() || isSub())
return 0;
if (isMul() || isDiv())
return 1;
throw new RuntimeException("This is not +, -, *, /");
}
@Override
public String toString() {
return value != null ? value.toString() : null;
}
}
/**
* 計算結果
*
* @param calStr
* @return
*/
public int calculate(String calStr) {
List<Item> infixes = parse(calStr);
List<Item> postfixes = infix2postfix(infixes);
return calculateByPostfix(postfixes);
}
/**
* 利用正則表達式將待計算的字符串轉化為List<Item>形式 ,如 10/2 -> [10, /, 2]
*
* @param calStr
* @return
*/
private List<Item> parse(String calStr) {
Pattern pattern = Pattern.compile("\\D|\\d+");
Matcher m = pattern.matcher(calStr);
List<Item> items = new ArrayList<Item>();
while (m.find()) {
items.add(new Item(m.group(0)));
}
return items;
}
/**
* 中綴表達式轉換為后綴表達式
* <p>
* 1.當讀到一個操作數時,立即將它放到輸出中。讀到的是操作符則需要接著判斷是否該入棧。讀到的是左圓括號則入棧。<br>
* 2.如果遇到一個右括號,那么就將棧中元素彈出并輸出直至遇到左括號為止。但是這個左括號只被彈出,并不輸出。<br>
* 3.在讀到操作符時,如果此操作符優先級小于或等于此時棧頂操作符,則將棧中元素彈出直至(1)遇到左括號或者(2)棧頂元素為更低優先級或者(3)
* 棧為空為止。操作符中,'+''-'優先級最低,'('')'優先級最高。 <br>
* 4.如果讀到輸入的末尾,將棧元素彈出直到該棧變成空棧,將符號寫到輸出中。
*
* @return
*/
private List<Item> infix2postfix(List<Item> infixes) {
List<Item> postfixes = new ArrayList<Item>();
Stack<Item> stack = new Stack<Item>();
for (Item item : infixes) {
if (item.isNumber()) {
postfixes.add(item);
} else if (item.isRightBracket()) {
// ) 右括號,將棧中元素彈出直至左括號,且左括號和右括號不加入到后綴表達式中
while (true) {
Item tmp = stack.pop();
if (tmp.isLeftBracket())
break;
postfixes.add(tmp);
}
} else if (item.isLeftBracket()) {
// ( 左括號,將左括號入棧
stack.push(item);
} else {
// 當前操作符為 +, -, *, /,
if (stack.isEmpty()) {
// 操作符棧為空,則將當前操作符壓入棧
stack.push(item);
continue;
}
Item top = stack.peek();
if (top.isLeftBracket()) {
// 操作符棧頂為左括號(,則將當前操作符壓入棧
stack.push(item);
continue;
}
if (item.getPriority() <= top.getPriority()) {
// 如果此操作符(+,-,*,/)優先級小于或等于此時棧頂操作符
// 則將棧中元素彈出直至(1)遇到左括號或者(2)棧頂元素為更低優先級或者(3)棧為空為止
// 并將彈出的元素加入后綴表達式中,將當前操作符壓入棧中
while (true) {
Item tmp = stack.peek();
if (tmp.isLeftBracket() || tmp.getPriority() < item.getPriority()) {
break;
}
postfixes.add(tmp);
stack.pop();
if (stack.isEmpty())
break;
}
stack.push(item);
} else {
// 如果當前操作符(+,-,*,/)優先級大于此時棧頂操作符,則將當前操作符壓入棧
stack.push(item);
}
}
}
// 如果棧中元素不為空,則將棧中元素全部彈出,加入后綴表達式中
while (!stack.isEmpty()) {
postfixes.add(stack.pop());
}
return postfixes;
}
/**
* 通過后綴表達式計算數值
* <p>
* 1. 從左到右遍歷表達式的每個數字和符號,遇到是數字則進棧,遇到是運算符則將棧頂兩個元素出棧,進行運算并將運算結果進棧<br>
* 2. 遍歷完后綴表達式,此時棧中剩余的數字就是運算結果
*
* @param postfixes
* @return
*/
private int calculateByPostfix(List<Item> postfixes) {
Stack<Integer> stack = new Stack<Integer>();
for (Item item : postfixes) {
if (item.isNumber()) {
stack.push(item.getNumber());
} else {
// 運算符
int num1 = stack.pop();
int num2 = stack.pop();
int result;
if (item.isAdd()) {
result = num2 + num1;
} else if (item.isSub()) {
result = num2 - num1;
} else if (item.isMul()) {
result = num2 * num1;
} else if (item.isDiv()) {
result = num2 / num1;
} else {
throw new IllegalArgumentException("Operator invalid : " + item.value);
}
stack.push(result);
}
}
return stack.pop();
}
public static void main(String[] args) {
SimpleCalcutor calcutor = new SimpleCalcutor();
String calStr = "9+(3-1)*3+10/2";
int result = calcutor.calculate(calStr);
System.out.println(result);
}
}
源碼github地址:
SimpleCalculator
參考鏈接:
利用棧將中綴表達式轉換成后綴表達式