Java數據結構(逆波蘭表達式)
最近在學習java數據結構的相關知識,記錄一下學習的內容,算是每天的總結,今天學習的是棧相關的內容,然後學習前綴,中綴,後綴表達式,下面介紹一下,這三種表達式的含義
- 中綴:中綴表達式,是我們最常見的表達式,就是日常算式,比如4+9-90*(5-1),這種結構是人類能夠了解並且計算的表達式但是計算機卻不好理解
- 後綴:後綴表達式,這裏先不說後綴表達式是什麼,先說怎麼用,對於中綴表達式1+((2+3)*4)-5,它對應的後綴表達式是:[1, 2, 90, +, 8, *, +, 230, -],在這裏我們從左向右掃描後綴表達式,對於數字就入棧,如果遇到符號,就彈出兩個元素進行計算,並將結果入棧,最後棧中的元素就是我們想要的運算結果,不相信可以演算:1,2,90,遇到+,進行運算:1,92,8,遇到乘號,進行運算:1,736,遇到加號開始運算:737,230,遇到減號,開始運算:507,也就是說,利用後綴表達式和棧,我們就可以利用計算機迭代輕易的求出表達式的解
- 前綴表達式,也成爲波蘭表達式,對於波蘭表達式,它同樣具備和後綴表達式相同的功能,那就是表達式的求值,只不過他需要我們從右向左遍歷手上的前綴表達式
下面是前綴表達式的求值代碼
public static int sovle(LinkedList<String> opt) {
Stack<String> stack = new Stack<String>();
for(String str:opt) {
if(str.matches("\\d+")) {
//如果是數字
stack.add(str);
}
else {
//如果是操作符
String o1 = stack.pop();
String o2 = stack.pop();
Integer result = func(o1, o2, str);
stack.push(result.toString());
}
}
String num = stack.pop();
return Integer.parseInt(num);
}
但是我們的問題往往是,我們如何才能拿到後綴表達式呢,或者說,如何才能將日常的中綴表達式轉化成後綴表達式呢,這就涉及到了今天學習的內容,爲了便於理解,我們舉一個不太恰當的例子,我們把後綴表達式的構建過程看成是梁山好漢的排座次的過程
我們將表達式中的符號進行簡單的類比,數字就相當於平民英豪,運算符相當於當過官的英豪,而對於括號可能有點不同,因爲括號不屬於運算符,我們暫且歸爲官的一種,但是他們是有家室的官員,因爲左右括號總是成對出現(這該死的愛情),下面介紹排位規則,一衆英豪來到忠義堂前,按照市井次序排成一列,也就是我們最常見的中綴表達式,按照從左到右的次序一個個上前驗明正身,對於平民英豪,因爲其貧苦出身,直接進入忠義堂,依次落座
那麼,1就做到第一把交椅,遇到第二個英豪,也就是加號,它屬於當過官的英豪,未免有些規矩,他的規矩就是,他們做過官的要坐在一塊,而且還要做領導層
所以就單獨做了一塊地,而且,根據做官的大小,他們也有尊卑之分,這體現到排位中就是,如果我的優先級小於等於我前面人的優先級,說明我的官銜大(一品大於二品,hhh原諒我在瞎編),我就會說:快滾出來,給小爺讓個位置,這時前面的操作符出棧,然後無家可歸(可憐),於是就去到隔壁,壓入隔壁的棧中,但是此時我前面又會有一個人(如果棧沒有空的話),此時我還會重複我的操作,但是對於左括號妹妹,我也會溫柔一些,會讓她坐我前面,但是對於,右括號哥哥,他就比較暴躁(比普通運算符暴躁,對於左括號都是直接進棧,她不會檢測前面坐的是誰,而對於右括號,他只允許他前面是左括號,於是就有了下面的樣子
下一個入棧的是右括號,他會講:我只要我的括號妹妹,你們都給我消失,所以前面的運算符們一個個出棧,去到隔壁,直到遇到左括號,左括號也出棧和右括號一塊,遠走高飛,於是我們看到了如下結果
最後市井英豪盡數入棧,但是分居不可取,乃將官府人員的棧頂元素,一次彈出,壓入隔壁,組成的就是後綴表達式的前身
下面是從韓順平老師那裏copy的官方說法:衆所周知,excel從來都只是一個畫圖工具
中綴轉後綴
* 案例:1+((2+3)*4)-5
* 1. 對於拿到手的中綴表達式,我們要先建立兩個棧s1,s2
* 2.從左向右掃描表達式,遇到數字就將其入棧s2
* 3.如果遇到的是操作符,就先比較其於s1棧頂操作符的優先級
* 3.1當然,有可能s1此時爲空棧,我們就可以直接將操作符入棧
* 3.2或者如果運算符是(也直接入棧即可
* 3.3如果如果運算優先級高於 棧頂元素,則將運算符入棧
* 3.4否則,將s1中操作符彈出,壓入到s2中,然後用新的棧頂元素
* 與之比較,重複步驟三
* 4. 如果是括號,對於左括號,直接入棧
* 如果遇到的是右括號,就直接將s1中的元素安撫彈出並壓入s2,直到遇到左括號
* 最後將兩個括號舍棄
* 5.將s1中剩餘元素,彈出,壓入s2
下面是代碼實現:
package com.shunping.poland;
import java.util.LinkedList;
import java.util.Stack;
public class Poland {
public static void main(String[] args) {
// TODO Auto-generated method stub
//拿到中綴表達式737-230
String str = "1 + ( ( 2 + 90 ) * 8 ) - 230";
//表達式打成list便於調用
LinkedList<String> pol = strTolist(str);
//進行逆波蘭轉換
//System.out.println(pol.toString());
LinkedList<String> opt = toPoland(pol);
System.out.println("後綴:"+opt.toString());
System.out.println("運算結果:"+sovle(opt));
}
public static LinkedList<String> strTolist(String str) {
LinkedList<String> result = new LinkedList<String>();
String[] strs = str.split(" ");
for(String item:strs) {
result.add(item);
}
return result;
}
public static LinkedList<String> toPoland(LinkedList<String> pol) {
Stack<String> opt = new Stack<String>();
LinkedList<String> num = new LinkedList<String>();
for(String str:pol) {
//如果掃描到數字就直接入棧num
if(str.matches("\\d+")) {
num.add(str);
}
else {
//如果是運算符或者括號
if(opt.isEmpty()||str.equals("(")) {
//如果運算符棧爲空,或者得到的是一個左括號,直接入棧
opt.push(str);
}
else if(str.equals(")")) {
//如果遇到右括號,就將opt中元素彈出並放入num,知道遇到左括號
while(!opt.peek().equals("(")) {
num.add(opt.pop());
}
//彈出左括號
opt.pop();
}
else {
//而且此時應該可以想到,棧內不存在一個配對的右括號
//就算有,也應該在前面匹配過了,最多有一個左括號
//此時str爲運算符,而且此時,棧一定不爲空
while(true) {
if(opt.isEmpty()) {
opt.push(str);
break;
}
else if(opt.peek().equals("(")||priority(str)>priority(opt.peek())) {
//如果此時棧頂爲左括號入棧
//如果此時操作符大於棧頂元素,則入棧
opt.push(str);
break;
}
else {
//此時運算符等級小於等於棧頂元素
//opt棧頂元素壓入num
num.add(opt.pop());
//這裏沒有跳出循環
//也就是說,當前操作符會接着和下一個棧頂元素比較
}
}
}
}
}
while(!opt.isEmpty()) {
//將操作符壓入num
num.add(opt.pop());
}
return num;
}
public static int priority(String opt) {
if(opt.equals("+")||opt.equals("-")) {
return 1;
}
else {
return 2;
}
}
public static int sovle(LinkedList<String> opt) {
Stack<String> stack = new Stack<String>();
for(String str:opt) {
if(str.matches("\\d+")) {
//如果是數字
stack.add(str);
}
else {
//如果是操作符
String o1 = stack.pop();
String o2 = stack.pop();
Integer result = func(o1, o2, str);
stack.push(result.toString());
}
}
String num = stack.pop();
return Integer.parseInt(num);
}
public static Integer func(String x, String y, String opt) {
Integer xx = Integer.parseInt(x);
Integer yy = Integer.parseInt(y);
if(opt.equalsIgnoreCase("+")) {
return xx+yy;
}
else if(opt.equalsIgnoreCase("-")) {
return yy-xx;
}
else if(opt.equalsIgnoreCase("*")) {
return xx*yy;
}
else if(opt.equalsIgnoreCase("/")) {
return yy/xx;
}
else {
return 0;
}
}
}