Java數據結構(逆波蘭表達式)

Java數據結構(逆波蘭表達式)
最近在學習java數據結構的相關知識,記錄一下學習的內容,算是每天的總結,今天學習的是棧相關的內容,然後學習前綴,中綴,後綴表達式,下面介紹一下,這三種表達式的含義

  1. 中綴:中綴表達式,是我們最常見的表達式,就是日常算式,比如4+9-90*(5-1),這種結構是人類能夠了解並且計算的表達式但是計算機卻不好理解
  2. 後綴:後綴表達式,這裏先不說後綴表達式是什麼,先說怎麼用,對於中綴表達式1+((2+3)*4)-5,它對應的後綴表達式是:[1, 2, 90, +, 8, *, +, 230, -],在這裏我們從左向右掃描後綴表達式,對於數字就入棧,如果遇到符號,就彈出兩個元素進行計算,並將結果入棧,最後棧中的元素就是我們想要的運算結果,不相信可以演算:1,2,90,遇到+,進行運算:1,92,8,遇到乘號,進行運算:1,736,遇到加號開始運算:737,230,遇到減號,開始運算:507,也就是說,利用後綴表達式和棧,我們就可以利用計算機迭代輕易的求出表達式的解
  3. 前綴表達式,也成爲波蘭表達式,對於波蘭表達式,它同樣具備和後綴表達式相同的功能,那就是表達式的求值,只不過他需要我們從右向左遍歷手上的前綴表達式
    下面是前綴表達式的求值代碼
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;
		}
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章