整體把握
數組、鏈表、樹等等都適用於數據庫應用中作數據記錄,常用來記錄對應於現實世界的數據;而棧、隊列及優先級隊列更多地是作為程序員的工具來使用(用最合適的工具干活),以簡化某些程序操作。
棧、隊列及優先級隊列都可以使用數組鏈表來實現,優先級隊列通常使用堆實現。
在棧、隊列及優先級隊列中,訪問是受限制的,在某一時刻只有某一個特定的數據項可以被讀取或刪除。
棧
應用:單詞逆序;解析源代碼時檢驗括號匹配;解析算術表達式;棧操作還嵌入在微處理器中,比如當調用一個方法時返回值和參數壓入棧,方法結束時,那些數據出棧。
解析算術表達式:
算術表達式求值通常都是先轉換為后綴表達式,再求后綴表達式的值;在中綴表表達式轉換為后綴表達式以及求后綴表達式的值的過程里,棧都是很有用的工具。
中綴表表達式 | 后綴表達式 |
((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 |
實現
數組實現,棧內有一個top記錄棧頂的數據頂,插入++top,刪除top--
單鏈表實現,push()對應insertFisrt(),pop()對應deleteFirst()
效率
入棧出棧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()); } } }
隊列
應用:
因特網數據包等待傳送;操作系統里有很多隊列,打印作業的打印隊列;文字處理軟件有一個容納鍵入內容的隊列,隊列保證了鍵入內容的順序不會改變。
隊列也可以用於模擬真實世界,比如模擬銀行排隊、飛機等待起飛。
實現:
數組實現,通常的作法是通過移動隊頭隊尾的指針保持數據項的位置不變,這樣必須使用環繞式處理。
雙端鏈表實現,insert()對應insertLast(),remove對應deleteFirst()
效率:
插入刪除數據項的時間復雜度都是O(1)
雙端隊列
頭尾都可以插入和刪除,比如可能有方法insertLeft()/insertRight、removeLeft()/removeRight()
優先級隊列
優先級隊列其實就是有序隊列,有升序優先級隊列和降序優先級隊列;它允許訪問最小(最大)的數據項。
應用:
在搶先式多任務操作系統中,程序排列在優先級隊列中,這樣優先級最高的程序就會先得到時間片運行。
很多情況下需要訪問具有最小關鍵字值的數據項,比如尋找最便宜的方法或最短的路徑去做某件事。
實現:
除了可以快速訪問最小關鍵字值的數據項,優先級隊列還應該實現相當快的插入數據項,因此優先級隊列通常使用椎來實現。
數組實現1——>有序數組實現,不需要使用環繞指針,插入慢,刪除快
數組實現2——>無序數組實現,插入快,刪除慢,因為要尋找最小值
有序鏈表實現