資料結構與演算法:棧
阿新 • • 發佈:2020-08-19
棧(stack)又名堆疊,它是一種運算受限的線性表。限定僅在表尾進行插入和刪除操作的線性表。這一端被稱為棧頂,相對地,把另一端稱為棧底。向一個棧插入新元素又稱作進棧、入棧或壓棧,它是把新元素放到棧頂元素的上面,使之成為新的棧頂元素;從一個棧刪除元素又稱作出棧或退棧,它是把棧頂元素刪除掉,使其相鄰的元素成為新的棧頂元素。
使用陣列模擬棧
public class ArrayStackDemo { public static void main(String[] args) { //測試陣列模擬棧 ArrayStack arrayStack = new ArrayStack(5);boolean loop = true; String key = ""; Scanner scanner = new Scanner(System.in); while (loop){ System.out.println("list:遍歷棧內資料"); System.out.println("exit:退出程式"); System.out.println("push:向棧內推入資料"); System.out.println("pop:從棧內取出資料"); key= scanner.next(); switch (key){ case "list": arrayStack.list(); break; case "push": int value = scanner.nextInt(); arrayStack.push(value); break;case "pop": try { int result = arrayStack.pop(); System.out.printf("取出的資料為:%d\n", result); }catch (Exception e){ System.out.println(e.getMessage()); } break; case "exit": scanner.close(); loop = false; break; default: break; } } System.out.println("程式已退出!"); } } //陣列模擬棧的類 class ArrayStack{ private int maxSize; //棧的最大容量 private int[] arrayStack; //陣列模擬棧 private int top = -1; //模擬棧的頂端指標,指向棧的頂端,預設為-1,即棧空 public ArrayStack(int maxSize) { this.maxSize = maxSize; arrayStack = new int[maxSize]; } /** * 因為棧是從索引為0的底端開始壓入資料的,所以 * 當頂端指標為maxSize-1時,棧為滿 * 當頂端指標為-1時,棧為空 */ public boolean isFull(){ //判斷是否為滿 return top == maxSize-1; } public boolean isEmpty(){ //判斷是否為空 return top == -1; } /** * 思路: * 1. 頂端指標top向後移一位 * 2. 將資料存放在指標指向的位置 * @param data */ public void push(int data){ //向棧內壓入資料 if (isFull()){ System.out.println("棧滿!"); return; } top++; arrayStack[top] = data; } /** * 思路: * 1. 將頂端指標top指向的位置的資料彈出 * 2. 將頂端指標top向前移一位 * @return */ public int pop(){ if (isEmpty()){ throw new RuntimeException("棧為空!"); } int result = arrayStack[top]; top--; return result; } public void list(){ //遍歷陣列模擬棧 if (isEmpty()){ System.out.println("棧為空!"); return; } for (int i=top; i>=0; i--){ System.out.printf("arrayStack[%d]=%d\n", i, arrayStack[i]); } } }
棧的應用——表示式求值
在我們日常中使用的表示式,如:(30+2)*5-16/4,稱做中綴表示式。它對於我們來說是易理解的,但對於計算機來說卻是非常複雜的,所以我們需要把表示式換做在計算器看來簡單的易懂的字尾表示式(逆波蘭表示式),如:30 2 + 5 * 16 4 / -。而棧就能幫我們把中綴表示式轉換成字尾表示式。
中綴表示式轉字尾的邏輯:
是以從左到右,按運算先後的順序來轉換的,運算子則放在運算數的後面,如 a+b-c 就是 a b + c -
以(30+2)*5-16/4為例,流程為
-
30+2 轉換為 30 2 +
-
(30+2)*5 轉換為 30 2 + 5 *
-
16/4 轉換為 16 4 /
-
(30+2)*5-16/4 轉換為 30 2 + 5 * 16 4 / -
程式碼實現
public class RPNCalculator { public static void main(String[] args) { String expression = "(30+2)*5-16/4"; //對應的字尾表示式為"30 2 + 5 * 16 4 / -" List<String> expressionList = splitToArrayList(expression); System.out.println(expressionList);//輸出原先的中綴表示式 List<String> suffixExpression = toSuffixExpression(expressionList); System.out.println(suffixExpression);//輸出轉換後的字尾表示式 System.out.println(expression+"="+calculator(suffixExpression));//輸出計算後的最終結果 } /** * 將中綴表示式轉為字尾表示式 * 邏輯思路: * 遍歷中綴表示式集合: * 1.如果是數字,直接存進字尾表示式集合中 * 2.如果是括號"()": * 1)如果是"(", 直接壓入操作符棧中 * 2)如果是")", 則遍歷操作符棧,檢視操作符棧的頂端是否是"(", * 如果不是則把頂端彈出新增到字尾表示式集合中,是則直接把"("從棧彈出即可 * 3.如果是其他操作符: * 1)如果操作符棧為空,則直接壓入棧中 * 2)如果不為空,則和棧頂操作符比較優先順序,如果棧頂操作符優先順序>=傳入的操作符優先順序, * 則先將棧頂操作符彈出並新增到字尾表示式集合中,再把傳入操作符壓入棧中 * 不是則直接壓入棧中 * 遍歷完中綴表示式集合後,再把操作符棧中剩餘的操作符彈出並新增到字尾表示式集合中 * @param expression 中綴表示式集合 * @return */ public static List<String> toSuffixExpression(List<String> expression){ Stack<String> operator = new Stack<>();//操作符棧,用於儲存操作符 List<String> suffixExpression = new ArrayList<>();//字尾表示式陣列集合 for (String item : expression){//遍歷中綴表示式中的每個資料 if (item.matches("^-?\\d+$")){//使用正則表示式判斷該字串是否是整數 suffixExpression.add(item);//如果是整數則直接加集合中 }else if (item.equals("(")){ operator.push(item);//如果是(,則壓入操作符棧中 }else if (item.equals(")")){ while( !operator.peek().equals("(") ){//判斷棧頂是否是"(" suffixExpression.add(operator.pop());//如果不是則彈出棧頂並新增到字尾表示式集合中 } operator.pop();//最後在把棧底的"("彈出 }else {//非括號的其他操作符 //棧為空 或 優先順序>=棧頂的操作符 則將棧頂操作符彈出並新增到字尾表示式集合中 while (!operator.isEmpty() && priority(item) <= priority(operator.peek())){ suffixExpression.add(operator.pop()); } operator.push(item);//最後再把操作符壓入棧中 } } while ( !operator.isEmpty() ){//把操作符棧中剩下的操作符彈出並新增到字尾表示式集合中 suffixExpression.add(operator.pop()); } return suffixExpression; } //計算方法 public static int calculator(List<String> suffixExpression){ Stack<Integer> numStack = new Stack<>();//用於儲存表示式中每段計算的結果 int result = 0; for (String item : suffixExpression){//遍歷字尾表示式集合 if (item.matches("^-?\\d+$")){//判斷是否是整數 numStack.push(Integer.parseInt(item));//如果是則直接壓入棧中 }else { //如果是操作符,則從棧中彈出倆個數字並計算結果 int num1 = numStack.pop(); int num2 = numStack.pop(); switch (item){ case "+": result = num1 + num2; break; case "-": result = num2 - num1; break; case "*": result = num1 * num2; break; case "/": result = num2 / num1; break; default: throw new RuntimeException("表示式含有未能識別的符號!"); } numStack.push(result);//再把計算後的結果壓入棧中 } } //最後棧中剩餘的唯一資料就是表示式最後的計算結果 return numStack.pop(); } //將表示式的字串分割成陣列集合 public static List<String> splitToArrayList(String expression){ List<String> expressionList = new ArrayList<>(); int index = 0;//字串下標,用於字串內的字元的鎖定 char c = ' '; String s = "";//用於拼接數字 while (index < expression.length()){//判斷下標是否已超出字串長度 c = expression.charAt(index);//根據下標從字串取出相應的字元 if (c > 47 && c < 58){//根據ascii碼判斷字元是否是數字 s += c;//把數字新增到拼接字串 while (index<expression.length()-1 && expression.charAt(index+1)>47 && expression.charAt(index+1)<58){//判斷是否是多位數 //如果是多位數,將下個下標的字元也新增進拼接字串 s += expression.charAt(index+1); index++; } expressionList.add(s); s = "";//將拼接字串清空 }else {//如果為其他字元則直接新增進集合中 expressionList.add(c+""); } index ++; } return expressionList; } //返回各個操作符的優先順序 public static int priority(String s){ switch (s){ case "+": case "-": return 1; case "*": case "/": return 2; case "(": case ")": return 0; default: throw new RuntimeException("表示式含有未能識別的符號!"); } } }
翻譯 朗讀 複製 正在查詢,請稍候…… 重試 朗讀 複製 複製 朗讀 複製 via 谷歌翻譯(國內) 譯