1. 程式人生 > >棧、佇列及優先順序佇列

棧、佇列及優先順序佇列

抽象資料型別(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()


優先順序佇列 

優先順序佇列其實就是有序佇列,有升序優先順序佇列和降序優先順序佇列;它允許訪問最小(最大)的資料項。

應用

在搶先式多工作業系統中,程式排列在優先順序佇列中,這樣優先順序最高的程式就會先得到時間片執行。
很多情況下需要訪問具有最小關鍵字值的資料項,比如尋找最便宜的方法或最短的路徑去做某件事。

實現

除了可以快速訪問最小關鍵字值的資料項,優先順序佇列還應該實現相當快的插入資料項,因此優先順序佇列通常使用椎來實現。
如果用陣列實現優先順序佇列(不需要使用環繞指標),插入慢,刪除快。
另外一種陣列實現有序佇列,不按順序排列資料項,插入快,刪除慢,因為要尋找最小值。