數組、鏈表、樹等資料結構適用於儲存資料庫應用中的資料記錄,它們常常用於記錄那些現實世界的對象和活動的資料,便與資料的訪問:插入、刪除和尋找特定資料項目
而棧和隊列更多的是作為程式員的工具來使用。他們主要作為構思演算法的協助工具輔助,而不是完全的資料存放區工具。這些資料結構的生命週期比那些資料庫類型的結構要短很多。在程式操作執行期間它們才被建立,通常它們去執行某項特殊的任務,當任務完成後就被銷毀
棧和隊列的訪問是受限制的,即在特定時刻只有一個資料項目可以被讀取或刪除
棧和隊列是比數組和其他資料結構更加抽象的結構,是站在更高的層面對資料進行組織和維護
棧的主要機制可用數組來實現,也可以用鏈表來實現。優先順序隊列的內部實現可以用數組或者一種特別的樹——堆來實現。
先來瞭解棧的概念和執行個體,然後分別深入理解隊列和優先順序隊列
棧只允許訪問一個資料項目:即最後插入的資料。移除這個資料項目後才能訪問倒數第二個插入的資料項目。它是一種“後進先出”的資料結構。
棧最基本的操作是出棧(Pop)、入棧(Push),還有其他擴充操作,如查看棧頂元素,判斷棧是否為空白、是否已滿,讀取棧的大小等
下面我們就用數組來寫一個棧操作的封裝類
public class Stack { private int size; //棧的大小 private int top; //棧頂元素的下標 private int [] stackArray; //棧的容器 //建構函式 public Stack(int size){ stackArray = new int [size]; top = -1; //初始化棧的時候,棧內無元素,棧頂下標設為-1 this.size = size; } //入棧,同時,棧頂元素的下標加一 public void push(int elem){ stackArray[++top] = elem; //插入棧頂 } //出棧,刪除棧頂元素,同時,棧頂元素的下標減一 public int pop(){ return stackArray[top--]; } //查看棧頂元素,但不刪除 public int peek(){ return stackArray[top]; } //判空 public boolean isEmpty(){ return (top == -1); } //判滿 public boolean isFull(){ return (top == size-1); } }
上例中,沒有對可能的異常進行處理,需要由編程人員保證程式的正確性,比如,才出棧前需要應該保證棧中有元素,在入棧前應保證棧沒有滿
入棧操作
出棧操作
棧通常用於解析某種類型的文本串。通常,文本串是用電腦語言寫的程式碼,而解析它們的程式就是編譯器
下面我們來用棧來實現一個經典的應用:分隔字元匹配。想一下在Eclipse編程時,如果我們寫的代碼中如果多了一個“{”,後者少了一個“}”,或者括弧的順序錯亂,都會報錯。接下來我們就用棧來類比這種分隔字元匹配
分隔字元匹配程式從字串中不斷地讀取程式,每次讀取一個字元,若發現它是左分隔字元({、[、(),將它壓入棧中。當讀到一個右分隔字元時()、]、}),彈出棧頂元素,並且查看它是否和該右分隔字元匹配。如果它們不匹配,則程式報錯。如果到最後一直存在著沒有被匹配的分隔字元,程式也報錯
我們來看下面這個正確的字串,在棧中的變化過程:
a{b(c[d]e)f}
所讀字元 棧中內容
a 空
{ {
b {
( {(
c {(
[ {([
d {([
] {(
e {(
) {
f {
} 空
最後出現的左分隔字元需要被最先匹配,這符合棧“後進先出”的規則
在本例中,要處理的是字元,所以需要對上面的Stack類進行修改,需要將存放元素的數組改為char類型,並把相關方法的參數類型改為char類型,其餘不變
public class Stack { private int size; //棧的大小 private int top; //棧頂元素的下標 private char [] stackArray; //棧的容器 //建構函式 public Stack(int size){ stackArray = new char [size]; top = -1; //初始化棧的時候,棧內無元素,棧頂下標設為-1 this.size = size; } //入棧,同時,棧頂元素的下標加一 public void push(char elem){ stackArray[++top] = elem; //插入棧頂 } //出棧,刪除棧頂元素,同時,棧頂元素的下標減一 public char pop(){ return stackArray[top--]; } //查看棧頂元素,但不刪除 public char peek(){ return stackArray[top]; } //判空 public boolean isEmpty(){ return (top == -1); } //判滿 public boolean isFull(){ return (top == size-1); } }
然後寫一個類來封裝分隔字元匹配的操作:
public class BrecketChecker { private String input; //儲存待檢查的字串 //構造方法,接受待檢查的字串 public BrecketChecker(String in){ this.input = in; } //檢查分隔字元匹配的方法 public void check(){ int strLength = input.length(); Stack stack = new Stack(strLength); for(int i=0;i<strLength;i++){ char ch =input.charAt(i); //一次擷取串中的單個字元 switch(ch){ case '{' : case '[' : case '(' : //如果為左分隔字元,壓入棧 stack.push(ch); break; case '}' : case ']' : case ')' : //如果為右分隔字元,與棧頂元素進行匹配 if(!stack.isEmpty()){ charchx = stack.pop(); if((ch== '{' && chx != '}')|| (ch == '(' && chx != ')')|| (ch == '[' && chx != ']') ){ System.out.println("匹配出錯!字元:"+ch+",下標:"+i); } }else{ System.out.println("匹配出錯!字元:"+ch+",下標:"+i); } default : break; } } if(!stack.isEmpty()){ //匹配結束時如果棧中還有元素,證明右分隔字元缺失 System.out.println("有括弧沒有關閉!"); } } }
測試類別
public static void main(String[] args) { System.out.println("輸入需要檢測的字串:"); String str = getString(); BrecketChecker checker = newBrecketChecker(str); checker.check(); } public static String getString(){ String str = ""; try{ InputStreamReader reader =new InputStreamReader(System.in); BufferedReader bReader = newBufferedReader(reader); str = bReader.readLine(); }catch(IOException e){ e.printStackTrace(); } return str; }