棧、佇列及優先順序佇列
抽象資料型別(ADT)
陣列、連結串列、樹等等都適用於資料庫應用中作資料記錄,常用來記錄對應於現實世界的資料;而棧、佇列及優先順序佇列更多地是作為程式設計師的工具來使用(用最合適的工具幹活),以簡化某些程式操作。
棧、佇列及優先順序佇列都可以使用陣列連結串列來實現,優先順序佇列通常使用堆實現。
在棧、佇列及優先順序佇列中,訪問是受限制的,在某一時刻只有某一個特定的資料項可以被讀取或刪除。
棧
應用:解析原始碼時檢驗括號匹配;解析算術表示式;棧操作還嵌入在微處理器中,比如當呼叫一個方法時返回值和引數壓入棧,方法結束時,那些資料出棧。
解析算術表示式:
算術表示式求值通常都是先轉換為字尾表示式,再求字尾表示式的值;在中綴表表達式轉換為字尾表示式以及求字尾表示式的值的過程裡,棧都是很有用的工具。
中綴表表達式 | 字尾表示式 |
((A+B)*C)-D | AB+C*D- |
A+B*(C-D/(E+F)) | ABCDEF+/-*+ |
轉換字尾表示式規則:運算元不入棧,操作符入棧,在適當的時候出棧,比如中綴表示式1+2*3-5=,解析為字尾表示式過程如下表所示
步數 | 讀表示式 | var | 棧 | 備註 |
第一步 | 1 | 1 | ||
第二步 | 1+ | 1 | + | |
第三步 | 1+2 | 12 | + | |
第四步 | 1+2* | 12 | +* | |
第五步 | 1+2*3 | 123 | +* | |
第六步 | 1+2*3- | 123*+ | - | 讀到-時把*出棧,把+也出棧 |
第七步 | 1+2*3-5 | 123*+5 | - | |
第八步 | 1+2*3-5= | 123*+5- | 讀到=時把-出棧 |
字尾表示式求值規則:運算元入棧、遇到操作符從棧中提出兩個運算元執行計算,結果入棧,計算123*+5-的值如下表所示
步數 | 讀表示式 | 棧 |
第一步 | 1 | 1 |
第二步 | 12 | 12 |
第三步 | 123 | 123 |
第四步 | 123* | 16 |
第五步 | 123*+ | 7 |
第六步 | 123*+5 | 75 |
第七步 | 123*+5- | 2 |
效率
入棧出棧O(1),也就是說,所耗的時間不依賴於棧中資料項的個數,棧不需要比較和移動操作。
程式碼實現
package MyTest; public class Stack { private int[] dataArr; private int initCapacity; private int top; public Stack(int initCapacity) { dataArr = new int[initCapacity]; top = -1; this.initCapacity = initCapacity; } public void push(int element) { dataArr[++top] = element; } public int pop() { return dataArr[top--]; } public int peek() { return dataArr[top]; } public boolean isEmpty() { return top == -1; } public boolean isFull() { return top == initCapacity - 1; } } class MyTest { public static void main(String[] args) { Stack stack = new Stack(20); stack.push(200); stack.push(300); stack.push(404); stack.push(555); stack.push(606); System.out.print(stack.peek() + " "); System.out.print(stack.peek() + " \n"); while (!stack.isEmpty()) { int pop = stack.pop(); System.out.print(pop + " "); } if (!stack.isEmpty()) { System.out.print(stack.peek()); } else { System.out.println(); System.out.print("Stack is empty"); } } } class WordReverser { public static void main(String[] args) { String str = wordReverser("中華人民共和國"); System.out.println(str); } private static String wordReverser(String str) { Stack stack = new Stack(20); for (int i = 0; i < str.length(); i++) { stack.push(str.charAt(i)); } str = ""; while (!stack.isEmpty()) { str += (char) stack.pop(); } return str; } } class Compiler { private static String src1 = "c[d]"; // correct,正確的 private static String src2 = "a{b[c]d}e"; // correct private static String src3 = "a{b(c]d}e"; // not correct; doesn't match private static String src4 = "a[b{c}d]e}"; // not correct; nothing matches final } private static String src5 = "a{b(c)"; // not correct; nothing mathches opening { private static Stack stack = new Stack(10); public static void main(String[] args) { /* * 結果有四種情況 * 1、正確 * 2、不匹配 * 3、左括號多餘,右括號多餘 */ compile(src5); } public static void compile(String src) { char c = 0; for (int i = 0; i < src.length(); i++) { c = src.charAt(i); switch (c) { case '{': case '[': case '(': stack.push(c); break; case '}': case ']': case ')': if (stack.isEmpty()) { System.out.println("not correct; nothing matches final " + (char)c); return; } else { int pop = stack.pop(); if (!(pop == '{' && c == '}' || pop == '[' && c == ']' || pop == '(' && c == ')')) { System.out.println("not correct; doesn't match"); return; } } } } if (stack.isEmpty()) { System.out.println("correct"); } else { System.out.println("not correct; nothing mathches opening " + (char) stack.pop()); } } }View Code
佇列
應用:
因特網資料包等待傳送;作業系統裡有很多佇列,列印作業的列印佇列;文書處理軟體有一個容納鍵入內容的佇列,佇列保證了鍵入內容的順序不會改變。
佇列也可以用於模擬真實世界,比如模擬銀行排隊、飛機等待起飛。
實現:
用陣列實現佇列,通常的作法是通過移動隊頭隊尾的指標保持資料項的位置不變,這樣必須使用環繞式處理。
效率:
插入刪除資料項的時間複雜度都是O(1)
雙端佇列
頭尾都可以插入和刪除,比如可能有方法insertLeft()/insertRight、removeLeft()/removeRight()
優先順序佇列
優先順序佇列其實就是有序佇列,有升序優先順序佇列和降序優先順序佇列;它允許訪問最小(最大)的資料項。
應用:
在搶先式多工作業系統中,程式排列在優先順序佇列中,這樣優先順序最高的程式就會先得到時間片執行。
很多情況下需要訪問具有最小關鍵字值的資料項,比如尋找最便宜的方法或最短的路徑去做某件事。
實現:
除了可以快速訪問最小關鍵字值的資料項,優先順序佇列還應該實現相當快的插入資料項,因此優先順序佇列通常使用椎來實現。
如果用陣列實現優先順序佇列(不需要使用環繞指標),插入慢,刪除快。
另外一種陣列實現有序佇列,不按順序排列資料項,插入快,刪除慢,因為要尋找最小值。