天天看點

資料結構(Java實作)-詳解棧(實作中綴表達式電腦、逆波蘭電腦 中綴表達式轉逆波蘭表達式過程)1、棧(stack)的介紹2、棧實作電腦(運算中綴表達式)3、字首、中綴和字尾表達式4、逆波蘭電腦

目錄

1、棧(stack)的介紹

1.1入棧和出棧的概念

1.2棧的應用場景

1.3棧的快速入門

2、棧實作電腦(運算中綴表達式)

1、提出問題

2、使用棧完成表達式的思路

3、按照思路圖解驗證一個表達式的運算

4、實作代碼:

3、字首、中綴和字尾表達式

1、字首表達式(波蘭式)

2、中綴表達式

3、字尾表達式(逆波蘭式)

4、逆波蘭電腦

1、中綴表達式轉為字尾表達式

2、将得到的字尾表達式進行運算

3、代碼實作:

1、棧(stack)的介紹

  1. 棧是一個先入後出、後入先出的有序清單。
  2. 棧是限制線性表中元素的插入和删除隻能線上性表的同一端進行的一種特殊線性表。允許插入和删除的一端為變化端,稱為棧頂(Top),另一端為固定端,稱為棧底(Bottom)。
  3. 根據棧的定義可知,最先放入棧中的元素在棧底,最後放入的元素在棧頂,而删除元素剛好相反,最後放入的元素最先删除     (類似于子彈匣)

1.1入棧和出棧的概念

1、入棧:

資料結構(Java實作)-詳解棧(實作中綴表達式電腦、逆波蘭電腦 中綴表達式轉逆波蘭表達式過程)1、棧(stack)的介紹2、棧實作電腦(運算中綴表達式)3、字首、中綴和字尾表達式4、逆波蘭電腦

2、出棧:

資料結構(Java實作)-詳解棧(實作中綴表達式電腦、逆波蘭電腦 中綴表達式轉逆波蘭表達式過程)1、棧(stack)的介紹2、棧實作電腦(運算中綴表達式)3、字首、中綴和字尾表達式4、逆波蘭電腦

1.2棧的應用場景

  1. 子程式的調用:在跳往子程式前,會先将下個指令的位址存到堆棧中,直到子程式執行完後再将位址取出,以回到原來的程式中。     
  2. 處理遞歸調用:和子程式的調用類似,隻是除了儲存下一個指令的位址外,也将參數、區域變量等資料存入堆棧中。
  3. 表達式的轉換[中綴表達式轉字尾表達式]與求值(實際解決)。
  4. 二叉樹的周遊。
  5. 圖形的深度優先(depth一first)搜尋法。

1.3棧的快速入門

      用數組模拟棧的使用,由于棧是一種有序清單, 當然可以使用數組的結構來儲存棧的資料内容, 下面我們就用數組模拟棧的出棧,入棧等操作。

實作思路和示意圖:

  1. 使用數組來模拟棧
  2. 定義一個top來表示棧頂,初始化為-1
  3. 入棧的操作,當資料加入到棧是,top++;stack[top] = data;
  4. 出棧的操作,int value = stack[top];top--; return value;
資料結構(Java實作)-詳解棧(實作中綴表達式電腦、逆波蘭電腦 中綴表達式轉逆波蘭表達式過程)1、棧(stack)的介紹2、棧實作電腦(運算中綴表達式)3、字首、中綴和字尾表達式4、逆波蘭電腦

代碼:

import java.util.Scanner;

public class ArrayStackDemo {

	public static void main(String[] args) {
		//測試一下ArrayStack 是否正确
		//先建立一個ArrayStack對象->表示棧
		ArrayStack stack = new ArrayStack(10);
		int key;
		boolean loop = true; //控制是否退出菜單
		Scanner scanner = new Scanner(System.in);	
		while(loop) {
			System.out.println("1: 周遊棧中的所有元素");
			System.out.println("2: 表示添加資料到棧(入棧)");
			System.out.println("3: 表示從棧取出資料(出棧)");
			System.out.println("4: 退出程式");
			System.out.println("請輸入你的選擇");
			key = scanner.nextInt();
			switch (key) {
			case 1:
				stack.list();
				break;
			case 2:
				System.out.println("請輸入一個數");
				int value = scanner.nextInt();
				stack.push(value);
				break;
			case 3:
				try {
					int res = stack.pop();
					System.out.printf("出棧的資料是 %d\n", res);
				} catch (Exception e) {
					// TODO: handle exception
					System.out.println(e.getMessage());
				}
				break;
			case 4:
				scanner.close();
				loop = false;
				break;
			default:
				break;
			}
		}		
		System.out.println("程式退出~~~");
	}
}
//定義一個 ArrayStack 表示棧
class ArrayStack {
	private int maxSize; // 棧的大小
	private int[] stack; // 數組,數組模拟棧,資料就放在該數組
	private int top = -1;// top表示棧頂,初始化為-1
	
	//構造器
	public ArrayStack(int maxSize) {
		this.maxSize = maxSize;
		stack = new int[this.maxSize];//初始化這個數組
	}
	
	//棧滿
	public boolean isFull() {
		return top == maxSize - 1;
	}
	//棧空
	public boolean isEmpty() {
		return top == -1;
	}
	//入棧-push
	public void push(int value) {
		//先判斷棧是否滿
		if(isFull()) {
			System.out.println("棧滿");
			return;
		}
		top++;
		stack[top] = value;
	}
	//出棧-pop, 将棧頂的資料傳回
	public int pop() {
		//先判斷棧是否空
		if(isEmpty()) {
			//抛出異常
			throw new RuntimeException("棧空,沒有資料~");
		}
		int value = stack[top];
		top--;
		return value;
	}
	//顯示棧的情況[周遊棧], 周遊時,需要從棧頂開始顯示資料
	public void list() {
		if(isEmpty()) {
			System.out.println("棧空,沒有資料~~");
			return;
		}
		//需要從棧頂開始顯示資料
		for(int i = top; i >= 0 ; i--) {
			System.out.printf("stack[%d]=%d\n", i, stack[i]);
		}
	}	
}
           

測試:

1: 周遊棧中的所有元素
2: 表示添加資料到棧(入棧)
3: 表示從棧取出資料(出棧)
4: 退出程式
請輸入你的選擇
2
請輸入一個數
4
1: 周遊棧中的所有元素
2: 表示添加資料到棧(入棧)
3: 表示從棧取出資料(出棧)
4: 退出程式
請輸入你的選擇
2
請輸入一個數
6
1: 周遊棧中的所有元素
2: 表示添加資料到棧(入棧)
3: 表示從棧取出資料(出棧)
4: 退出程式
請輸入你的選擇
2
請輸入一個數
8
1: 周遊棧中的所有元素
2: 表示添加資料到棧(入棧)
3: 表示從棧取出資料(出棧)
4: 退出程式
請輸入你的選擇
1
stack[2]=8
stack[1]=6
stack[0]=4
1: 周遊棧中的所有元素
2: 表示添加資料到棧(入棧)
3: 表示從棧取出資料(出棧)
4: 退出程式
請輸入你的選擇
3
出棧的資料是 8
1: 周遊棧中的所有元素
2: 表示添加資料到棧(入棧)
3: 表示從棧取出資料(出棧)
4: 退出程式
請輸入你的選擇
4
程式退出~~~
           

2、棧實作電腦(運算中綴表達式)

1、提出問題

計算式:[7*2*2-5+1-5+3-3]

資料結構(Java實作)-詳解棧(實作中綴表達式電腦、逆波蘭電腦 中綴表達式轉逆波蘭表達式過程)1、棧(stack)的介紹2、棧實作電腦(運算中綴表達式)3、字首、中綴和字尾表達式4、逆波蘭電腦

 請問:計算機底層是如何運算得到結果的?注意不是簡單的把算式列出運算,思考計算機是怎麼了解這個算式的(對計算機而言,它接收到的就是一個字元串)——棧

2、使用棧完成表達式的思路

資料結構(Java實作)-詳解棧(實作中綴表達式電腦、逆波蘭電腦 中綴表達式轉逆波蘭表達式過程)1、棧(stack)的介紹2、棧實作電腦(運算中綴表達式)3、字首、中綴和字尾表達式4、逆波蘭電腦

1、建立兩個棧,分别為數棧(存放數)符号棧(存放符号)

2、使用一個 index  值(索引),來周遊掃描我們的表達式

3、如果掃描的是一個數字就直接入棧

4、如果掃描的是一個符号,就分如下情況:

      a、如果發現目前的符号棧為空,就直接入棧

      b、如果符号棧有操作符,就進行比較:

  • 如果目前的操作符的優先級小于或者等于棧中的操作符, 就需要從數棧中pop出兩個數,在從符号棧中pop出一個符号,進行運算,将得到結果,入數棧,然後将目前的操作符入符号棧,
  • 如果目前的操作符的優先級大于棧中的操作符, 就直接入符号棧.

5、當表達式掃描完畢,就順序的從 數棧和符号棧中pop出相應的數和符号,并進行運算.

6、最後在數棧中隻有一個數字,就是表達式的結果

3、按照思路圖解驗證一個表達式的運算

驗證:3+2*6-2=13 

1、index掃描到數字3直接入棧

資料結構(Java實作)-詳解棧(實作中綴表達式電腦、逆波蘭電腦 中綴表達式轉逆波蘭表達式過程)1、棧(stack)的介紹2、棧實作電腦(運算中綴表達式)3、字首、中綴和字尾表達式4、逆波蘭電腦

2、index掃描到符号+,判斷目前的符号棧為空,就直接入棧

資料結構(Java實作)-詳解棧(實作中綴表達式電腦、逆波蘭電腦 中綴表達式轉逆波蘭表達式過程)1、棧(stack)的介紹2、棧實作電腦(運算中綴表達式)3、字首、中綴和字尾表達式4、逆波蘭電腦

3、index掃描到2,直接進棧

資料結構(Java實作)-詳解棧(實作中綴表達式電腦、逆波蘭電腦 中綴表達式轉逆波蘭表達式過程)1、棧(stack)的介紹2、棧實作電腦(運算中綴表達式)3、字首、中綴和字尾表達式4、逆波蘭電腦

4、掃描到*,*的優先級大于+,直接進棧

資料結構(Java實作)-詳解棧(實作中綴表達式電腦、逆波蘭電腦 中綴表達式轉逆波蘭表達式過程)1、棧(stack)的介紹2、棧實作電腦(運算中綴表達式)3、字首、中綴和字尾表達式4、逆波蘭電腦

5、掃描到6,直接進棧。

資料結構(Java實作)-詳解棧(實作中綴表達式電腦、逆波蘭電腦 中綴表達式轉逆波蘭表達式過程)1、棧(stack)的介紹2、棧實作電腦(運算中綴表達式)3、字首、中綴和字尾表達式4、逆波蘭電腦

6、掃描到-,-的優先級小于棧中的*,從數棧中pop出兩個數6和2,從符号棧中pop出*

運算:2*6=12 ,将12進棧,-進棧

資料結構(Java實作)-詳解棧(實作中綴表達式電腦、逆波蘭電腦 中綴表達式轉逆波蘭表達式過程)1、棧(stack)的介紹2、棧實作電腦(運算中綴表達式)3、字首、中綴和字尾表達式4、逆波蘭電腦

7、掃描到2,直接進棧

資料結構(Java實作)-詳解棧(實作中綴表達式電腦、逆波蘭電腦 中綴表達式轉逆波蘭表達式過程)1、棧(stack)的介紹2、棧實作電腦(運算中綴表達式)3、字首、中綴和字尾表達式4、逆波蘭電腦

8、依次pop出兩個數和一個符号進行運算(後一個數   符号   前一個數)

數棧pop出2和12    符号棧pop出-       運算:12-2=10   将10入棧

資料結構(Java實作)-詳解棧(實作中綴表達式電腦、逆波蘭電腦 中綴表達式轉逆波蘭表達式過程)1、棧(stack)的介紹2、棧實作電腦(運算中綴表達式)3、字首、中綴和字尾表達式4、逆波蘭電腦

數棧pop出10和3     符号棧pop出+     運算:3+10=13  将13入棧

資料結構(Java實作)-詳解棧(實作中綴表達式電腦、逆波蘭電腦 中綴表達式轉逆波蘭表達式過程)1、棧(stack)的介紹2、棧實作電腦(運算中綴表達式)3、字首、中綴和字尾表達式4、逆波蘭電腦

9、此時數棧中的13為最終運算結果,而符号棧棧空

注意:在測試過程中發現如果表達式中有多位數運算就會出問題。比如表達式中如果有數字13的話這裡的1和3就會當成兩個數分開入棧,後面的運算自然也會出問題。

解決方法:

  1. 在掃描到一個字元是數時不能立即入棧,向表達式中看向index指向字元的下一位,注意隻是看index指向字元的後一位而不是index++
  2. 如果index指向字元的下一位還是數就繼續掃描并将該數和之前的數進行字元拼接,如果下一位是符号就能入棧
  3. 定義一個字元串變量,用于拼接

4、實作代碼:

import java.util.Scanner;
public class Calculator {
	public static void main(String[] args) {
		System.out.println("請輸入一個僅限于加減乘除的數學表達式:");
		Scanner scanner = new Scanner(System.in);
		String expression = scanner.next(); 
		//建立兩個棧,數棧,一個符号棧
		CalculatorStack numStack = new CalculatorStack (10);
		CalculatorStack  operStack = new CalculatorStack (10);
		//定義需要的相關變量
		int index = 0;//用于掃描
		int num1 = 0; 
		int num2 = 0;
		int oper = 0;//儲存運算符
		int res = 0;//儲存每次的運算結果
		char ch = ' '; //将每次掃描得到char儲存到ch
		String keepNum = ""; //用于拼接 多位數
		//開始while循環的掃描expression
		while(true) {
			//依次得到expression 的每一個字元
			ch = expression.substring(index, index+1).charAt(0);
			//判斷ch是什麼,然後做相應的處理
			if(operStack.isOper(ch)) {//如果是運算符
				//判斷目前的符号棧是否為空
				if(!operStack.isEmpty()) {
					//如果符号棧有操作符,就進行比較,如果目前的操作符的優先級小于或者等于棧中的操作符,就需要從數棧中pop出兩個數,
					//在從符号棧中pop出一個符号,進行運算,将得到結果,入數棧,然後将目前的操作符入符号棧
					if(operStack.priority(ch) <= operStack.priority(operStack.peek())) {
						num1 = numStack.pop();
						num2 = numStack.pop();
						oper = operStack.pop();
						res = numStack.cal(num1, num2, oper);
						//把運算的結果入數棧
						numStack.push(res);
						//然後将目前的操作符入符号棧
						operStack.push(ch);
					} else {
						//如果目前的操作符的優先級大于棧中的操作符, 就直接入符号棧.
						operStack.push(ch);
					}
				}else {
					//如果為空直接入符号棧..
					operStack.push(ch); // 1 + 3
				}
			} else { 
				//如果是數有以下分析
				//分析思路
				//1. 當處理多位數時,不能發現是一個數就立即入棧,因為他可能是多位數
				//2. 在處理數,需要向expression的表達式的index 後再看一位,如果是數就進行掃描,如果是符号才入棧
				//3. 是以我們需要定義一個變量 字元串,用于拼接				
				//處理多位數
				keepNum += ch;		//拼接		
				//如果ch已經是expression的最後一位,就直接入棧
				if (index == expression.length() - 1) {
					numStack.push(Integer.parseInt(keepNum));
				}else{				
					//判斷下一個字元是不是數字,如果是數字,就繼續掃描,如果是運算符,則入棧
					//注意是看後一位,不是index++
					if (operStack.isOper(expression.substring(index+1,index+2).charAt(0))) {
						//如果後一位是運算符,則入棧 keepNum = "1" 或者 "123"
						numStack.push(Integer.parseInt(keepNum));
						//重要的!!!!!!, keepNum清空,友善後面再次掃描到多位數時的使用
						keepNum = "";
					}
				}
			}
			//讓index + 1, 并判斷是否掃描到expression最後.
			index++;
			if (index >= expression.length()) {
				break;
			}
		}
		
		//當表達式掃描完畢,就順序的從 數棧和符号棧中pop出相應的數和符号,并運作.
		while(true) {
			//如果符号棧為空,則計算到最後的結果, 數棧中隻有一個數字【結果】
			if(operStack.isEmpty()) {
				break;
			}
			num1 = numStack.pop();
			num2 = numStack.pop();
			oper = operStack.pop();
			res = numStack.cal(num1, num2, oper);
			numStack.push(res);//入棧
		}
		//将數棧的最後數,pop出,就是結果
		int res2 = numStack.pop();
		System.out.printf("表達式 %s = %d", expression, res2);
	}

}

//先建立一個棧,直接使用前面建立好
//定義一個 ArrayStack2 表示棧, 需要擴充功能
class CalculatorStack {
	private int maxSize; // 棧的大小
	private int[] stack; // 數組,數組模拟棧,資料就放在該數組
	private int top = -1;// top表示棧頂,初始化為-1
	
	//構造器
	public CalculatorStack(int maxSize) {
		this.maxSize = maxSize;
		stack = new int[this.maxSize];
	}
	
	//增加一個方法,可以傳回目前棧頂的值, 但是不是真正的pop
	public int peek() {
		return stack[top];
	}
	
	//棧滿
	public boolean isFull() {
		return top == maxSize - 1;
	}
	//棧空
	public boolean isEmpty() {
		return top == -1;
	}
	//入棧-push
	public void push(int value) {
		//先判斷棧是否滿
		if(isFull()) {
			System.out.println("棧滿");
			return;
		}
		top++;
		stack[top] = value;
	}
	//出棧-pop, 将棧頂的資料傳回
	public int pop() {
		//先判斷棧是否空
		if(isEmpty()) {
			//抛出異常
			throw new RuntimeException("棧空,沒有資料~");
		}
		int value = stack[top];
		top--;
		return value;
	}
	//顯示棧的情況[周遊棧], 周遊時,需要從棧頂開始顯示資料
	public void list() {
		if(isEmpty()) {
			System.out.println("棧空,沒有資料~~");
			return;
		}
		//需要從棧頂開始顯示資料
		for(int i = top; i >= 0 ; i--) {
			System.out.printf("stack[%d]=%d\n", i, stack[i]);
		}
	}
	//傳回運算符的優先級,優先級是程式員來确定, 優先級使用數字表示
	//數字越大,則優先級就越高.
	public int priority(int oper) {
		if(oper == '*' || oper == '/'){
			return 1;
		} else if (oper == '+' || oper == '-') {
			return 0;
		} else {
			return -1; // 假定目前的表達式隻有 +, - , * , /
		}
	}
	//判斷是不是一個運算符
	public boolean isOper(char val) {
		return val == '+' || val == '-' || val == '*' || val == '/';
	}
	//計算方法
	public int cal(int num1, int num2, int oper) {
		int res = 0; // res 用于存放計算的結果
		switch (oper) {
		case '+':
			res = num2 + num1;
			break;
		case '-':
			res = num2 - num1;// 注意順序
			break;
		case '*':
			res = num2 * num1;
			break;
		case '/':
			res = num2 / num1;
			break;
		default:
			break;
		}
		return res;
	}	
}
           
請輸入一個僅限于加減乘除的數學表達式:
2+4*12-4+6*14
表達式 2+4*12-4+6*14 = 130
           

3、字首、中綴和字尾表達式

1、字首表達式(波蘭式)

(1)、字首表達式又稱波蘭式,字首表達式的運算符位于操作數之前

(2)、舉例說明: (3+4)×5-6 對應的字首表達式就是 - × + 3 4 5 6

字首表達式的計算機求值:

    從右至左掃描表達式,遇到數字時,将數字壓入堆棧,遇到運算符時,彈出棧頂的兩個數,用運算符對它們做相應的計算(棧頂元素和次頂元素),并将結果入棧;重複上述過程直到表達式最左端,最後運算得出的值即為表達式的結果

例如: (3+4)×5-6 對應的字首表達式就是 - × + 3 4 5 6 , 針對字首表達式求值步驟如下:

  1. 從右至左掃描表達式"- × + 3 4 5 6 ",将6、5、4、3壓入堆棧
  2. 遇到+運算符,是以彈出3和4(3為棧頂元素,4為次頂元素),計算出3+4的值,得7,再将7入棧
  3. 接下來是×運算符,是以彈出7和5,計算出7×5=35,将35入棧
  4. 最後是-運算符,計算出35-6的值,即29,由此得出最終結果

2、中綴表達式

(1)、中綴表達式就是常見的運算表達式,如(3+4)×5-6

(2)、中綴表達式的求值是我們人最熟悉的,但是對計算機來說卻不好操作(前面我們講的案例就能看的這個問題),是以,在計算結果時,往往會将中綴表達式轉成其它表達式來操作(一般轉成字尾表達式.)

3、字尾表達式(逆波蘭式)

(1)、字尾表達式又稱逆波蘭表達式,與字首表達式相似,隻是運算符位于操作數之後

(2)、中舉例說明: (3+4)×5-6 對應的字尾表達式就是 3 4 + 5 × 6 –

(3)、再比如:

資料結構(Java實作)-詳解棧(實作中綴表達式電腦、逆波蘭電腦 中綴表達式轉逆波蘭表達式過程)1、棧(stack)的介紹2、棧實作電腦(運算中綴表達式)3、字首、中綴和字尾表達式4、逆波蘭電腦

字尾表達式的計算機求值:

從左至右掃描表達式,遇到數字時,将數字壓入堆棧,遇到運算符時,彈出棧頂的兩個數,用運算符對它們做相應的計算(次頂元素 和 棧頂元素),并将結果入棧;重複上述過程直到表達式最右端,最後運算得出的值即為表達式的結果

例如: (3+4)×5-6 對應的字尾表達式就是 3 4 + 5 × 6 - , 針對字尾表達式求值步驟如下:

  1. 從左至右掃描"3 4 + 5 × 6 - ",将3和4壓入堆棧;
  2. 遇到+運算符,是以彈出4和3(4為棧頂元素,3為次頂元素),計算出3+4的值,得7,再将7入棧;
  3. 将5入棧;
  4. 接下來是×運算符,是以彈出5和7,計算出7×5=35,将35入棧;
  5. 将6入棧;
  6. 最後是-運算符,計算出35-6(次頂-棧頂)的值,即29,由此得出最終結果

4、逆波蘭電腦

1、中綴表達式轉為字尾表達式

 字尾表達式适合計算式進行運算,但是人卻不太容易寫出來,尤其是表達式很長的情況下,是以在開發中,我們需要将 中綴表達式轉成字尾表達式

具體步驟:

1、初始化兩個棧:運算符棧s1和儲存中間結果的棧s2;

2、從左至右掃描中綴表達式;

3、遇到操作數時,将其壓入s2

4、遇到運算符時,比較其與s1棧頂運算符的優先級:

  • 如果s1為空,或棧頂運算符為左括号“(”,則直接将此運算符入棧;
  • 否則,若優先級比棧頂運算符的高,也将運算符壓入s1;
  • 否則,将s1棧頂的運算符彈出并壓入到s2中,再次轉到(4.1的操作)與s1中新的棧頂運算符相比較;    

5、遇到括号時:

  • 如果是左括号“(”,則直接壓入s1
  •  如果是右括号“)”,則依次彈出s1棧頂的運算符,并壓入s2,直到遇到左括号為止,此時将這一對括号丢棄

6、重複步驟2至5,直到表達式的最右邊

7、将s1中剩餘的運算符依次彈出并壓入s2

8、依次彈出s2中的元素并輸出,結果的逆序即為中綴表達式對應的字尾表達式

例如:将中綴表達式“1+((2+3)*4)-5”轉 換為字尾表達式的過程如下:

資料結構(Java實作)-詳解棧(實作中綴表達式電腦、逆波蘭電腦 中綴表達式轉逆波蘭表達式過程)1、棧(stack)的介紹2、棧實作電腦(運算中綴表達式)3、字首、中綴和字尾表達式4、逆波蘭電腦

是以結果為:"1 2 3 + 4 * + 5 –" 

2、将得到的字尾表達式進行運算

字尾表達式:"1 2 3 + 4 * + 5 –" 

運算過程(即字尾表達式的運算過程):

  1. 從左至右掃描"1 2 3 + 4 * + 5 – ",将1、2、3壓入堆棧;
  2. 遇到+運算符,是以彈出3和2(3為棧頂元素,2為次頂元素),計算出2+3的值,得5,再将5入棧;
  3. 将4入棧;
  4. 接下來是*運算符,是以彈出4和5,計算出4×5=20,将20入棧;
  5. 遇到+運算符,是以彈出20和1(20為棧頂元素,1為次頂元素),計算出20+1的值,得21,再将21入棧;
  6. 将5入棧;
  7. 最後是-運算符,彈出5和21(5為棧頂元素,21為次頂元素)21-5(次頂-棧頂)的值,即16,由此得出最終結果

3、代碼實作:

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class PolandNotation {
	public static void main(String[] args) {	
		//完成将一個中綴表達式轉成字尾表達式的功能
		//說明
		//1. 1+((2+3)×4)-5 => 轉成  1 2 3 + 4 × + 5 –
		//2. 因為直接對str 進行操作,不友善,是以 先将  "1+((2+3)×4)-5" =》 中綴的表達式對應的List
		//   即 "1+((2+3)×4)-5" => ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]
		//3. 将得到的中綴表達式對應的List => 字尾表達式對應的List
		//   即 ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]  =》 ArrayList [1,2,3,+,4,*,+,5,–]	
		String expression = "1+((2+3)*4)-5";//注意表達式 
		List<String> infixExpressionList = toInfixExpressionList(expression);
		System.out.println("中綴表達式對應的List=" + infixExpressionList); // ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]
		List<String> suffixExpreesionList = parseSuffixExpreesionList(infixExpressionList);
		System.out.println("字尾表達式對應的List" + suffixExpreesionList); //ArrayList [1,2,3,+,4,*,+,5,–] 	
		System.out.printf("運算結果=%d", calculate(suffixExpreesionList)); // ?	
	}
	//即 ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]  =》 ArrayList [1,2,3,+,4,*,+,5,–]
	//方法:将得到的中綴表達式對應的List => 字尾表達式對應的List
	public static List<String> parseSuffixExpreesionList(List<String> ls) {
		//定義兩個棧
		Stack<String> s1 = new Stack<String>(); // 符号棧
		//說明:因為s2 這個棧,在整個轉換過程中,沒有pop操作,而且後面我們還需要逆序輸出
		//是以比較麻煩,這裡我們就不用 Stack<String> 直接使用 List<String> s2
		List<String> s2 = new ArrayList<String>(); // 儲存中間結果的Lists2		
		//周遊ls
		for(String item: ls) {
			//如果是一個數,加入s2
			if(item.matches("\\d+")) {
				s2.add(item);
			} else if (item.equals("(")) {
				s1.push(item);
			} else if (item.equals(")")) {
				//如果是右括号“)”,則依次彈出s1棧頂的運算符,并壓入s2,直到遇到左括号為止,此時将這一對括号丢棄
				while(!s1.peek().equals("(")) {
					s2.add(s1.pop());
				}
				s1.pop();//!!! 将 ( 彈出 s1棧, 消除小括号
			} else {
				//當item的優先級小于等于s1棧頂運算符, 将s1棧頂的運算符彈出并加入到s2中,再次轉到(4.1)與s1中新的棧頂運算符相比較
				//問題:我們缺少一個比較優先級高低的方法
				while(s1.size() != 0 && Operation.getValue(s1.peek()) >= Operation.getValue(item) ) {
					s2.add(s1.pop());
				}
				//還需要将item壓入棧
				s1.push(item);
			}
		}	
		//将s1中剩餘的運算符依次彈出并加入s2
		while(s1.size() != 0) {
			s2.add(s1.pop());
		}
		return s2; //注意因為是存放到List, 是以按順序輸出就是對應的字尾表達式對應的List	
	}
	//方法:将 中綴表達式轉成對應的List
	//  s="1+((2+3)×4)-5";
	public static List<String> toInfixExpressionList(String s) {
		//定義一個List,存放中綴表達式 對應的内容
		List<String> ls = new ArrayList<String>();
		int i = 0; //這時是一個指針,用于周遊 中綴表達式字元串
		String str; // 對多位數的拼接
		char c; // 每周遊到一個字元,就放入到c
		do {
			//如果c是一個非數字,我需要加入到ls
			if((c=s.charAt(i)) < 48 ||  (c=s.charAt(i)) > 57) {
				ls.add("" + c);
				i++; //i需要後移
			} else { //如果是一個數,需要考慮多位數
				str = ""; //先将str 置成"" '0'[48]->'9'[57]
				while(i < s.length() && (c=s.charAt(i)) >= 48 && (c=s.charAt(i)) <= 57) {
					str += c;//拼接
					i++;
				}
				ls.add(str);
			}
		}while(i < s.length());
		return ls;//傳回
	}
	//将一個逆波蘭表達式, 依次将資料和運算符放入到 ArrayList中
	public static List<String> getListString(String suffixExpression) {
		//将 suffixExpression 分割
		String[] split = suffixExpression.split(" ");
		List<String> list = new ArrayList<String>();
		for(String ele: split) {
			list.add(ele);
		}
		return list;
		
	}	
	public static int calculate(List<String> ls) {
		// 建立給棧, 隻需要一個棧即可
		Stack<String> stack = new Stack<String>();
		// 周遊 ls
		for (String item : ls) {
			// 這裡使用正規表達式來取出數
			if (item.matches("\\d+")) { // 比對的是多位數
				// 入棧
				stack.push(item);
			} else {
				// pop出兩個數,并運算, 再入棧
				int num2 = Integer.parseInt(stack.pop());
				int num1 = Integer.parseInt(stack.pop());
				int res = 0;
				if (item.equals("+")) {
					res = num1 + num2;
				} else if (item.equals("-")) {
					res = num1 - num2;
				} else if (item.equals("*")) {
					res = num1 * num2;
				} else if (item.equals("/")) {
					res = num1 / num2;
				} else {
					throw new RuntimeException("運算符有誤");
				}
				//把res 入棧
				stack.push("" + res);
			}	
		}
		//最後留在stack中的資料是運算結果
		return Integer.parseInt(stack.pop());
	}
}
//編寫一個類 Operation 可以傳回一個運算符 對應的優先級
class Operation {
	private static int ADD = 1;
	private static int SUB = 1;
	private static int MUL = 2;
	private static int DIV = 2;	
	//寫一個方法,傳回對應的優先級數字
	public static int getValue(String operation) {
		int result = 0;
		switch (operation) {
		case "+":
			result = ADD;
			break;
		case "-":
			result = SUB;
			break;
		case "*":
			result = MUL;
			break;
		case "/":
			result = DIV;
			break;
		case "(":
			break;
		case ")":
			break;
		default:
			System.out.println("不存在該運算符" + operation);
			break;
		}
		return result;
	}
	
}
           
中綴表達式對應的List=[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]
字尾表達式對應的List[1, 2, 3, +, 4, *, +, 5, -]
運算結果=16
           

繼續閱讀