棧及其實現

1.棧介紹

棧是一種比較重要的線性結構。從數據結構的角度看,棧也是線性表,只不過它是操作受限的線性表。其限制是僅允許在表的一端進行插入和刪除操作,不允許在其他任何位置進行插入、查找、刪除等操作。

在表中進行插入、刪除操作的一端稱爲棧頂(top),棧頂保存的元素稱爲棧頂元素。相對的,棧的另一端稱爲棧底(bottom)。不含元素的空表稱爲空棧。

假設棧S=(a0,a1,…,an-1),則稱a0爲棧底元素,an-1爲棧頂元素。棧中元素按照a0,a1,…,an-1的次序進棧,退棧的第一個元素應爲棧頂元素。換句話說,棧的修改是按後進先出的原則進行的。因此,棧又稱爲後進先出(Last In First Out)的線性表(簡稱LIFO結構)。

下圖展示了一個棧及數據元素插入和刪除的過程:

這裏寫圖片描述

在上圖中,當ABCD均已入棧後,出棧時得到的序列爲DCBA,這就是後進先出。

棧的基本操作除了進棧push(),出棧pop()之外,還有判空isEmpty()、取棧頂元素peek()等操作。

2.棧的順序存儲與實現

和線性表類似,棧也有兩種存儲結構:順序存儲和鏈式存儲。

順序棧是使用順序存儲結構實現的棧,即利用一組地址連續的存儲單元依次存放棧中的數據元素。由於棧是一種特殊的線性表,因此在線性表的順序存儲結構的基礎上,選擇線性表的一端作爲棧頂即可。那麼根據數組操作的特性,選擇數組下標大的一端,即線性表順序存儲的表尾來作爲棧頂,此時入棧、出棧操作可以O(1)時間完成。

由於棧的操作都是在棧頂完成,因此在順序棧的實現中需要附設一個指針top來動態地指示棧頂元素在數組中的位置。通常top可以用棧頂元素所在的數組下標來表示,top=-1時表示空棧。

棧在使用過程中所需的最大空間難以估計,所以,一般構造棧的時候不應設定最大容量。一種合理的做法和線性表類似,先爲棧分配一個基本容量,然後在實際的使用過程中,當棧的空間不夠用時再倍增存儲空間。

下述代碼給出了棧的順序存儲的實現:

public class ArrayStack<T> {
    private final int DEFAULT_LEN = 8;// 數組的默認大小
    private T[] elements;// 數組
    private int top; // 棧頂指針

    @SuppressWarnings("unchecked")
    public ArrayStack() {
        top = -1;
        elements = (T[]) new Object[DEFAULT_LEN];
    }

    /**
     * 獲取棧的大小,即棧中數據元素的個數
     * 
     * @return
     */
    public int size() {
        return top + 1;
    }

    /**
     * 獲取堆棧是否爲空,爲空返回true,否則返回false
     * 
     * @return
     */
    public boolean isEmpty() {
        return top == -1;
    }

    /**
     * 入棧
     * 
     * @param e
     */
    public void push(T e) {
        if (size() >= elements.length) {
            // 棧滿
            ensureCapacity();
        }
        elements[++top] = e;
    }

    /**
     * 輔助方法,擴充容量
     */
    @SuppressWarnings("unchecked")
    private void ensureCapacity() {
        T[] a = (T[]) new Object[2 * elements.length + 1];
        System.arraycopy(elements, 0, a, 0, size());
        elements = a;
    }

    /**
     * 出棧
     * 
     * @return
     */
    public T pop() {
        if (isEmpty()) {
            throw new EmptyStackException();
        }
        T e = elements[top];
        elements[top--] = null;
        return e;
    }

    /**
     * 獲取棧頂元素
     * 
     * @return
     */
    public T peek() {
        if (isEmpty()) {
            throw new EmptyStackException();
        }
        return elements[top];
    }
}

以上基於數據實現的棧代碼並不難理解。由於有top指針的存在,所以size()isEmpty()方法均可在O(1)時間內完成。push()pop()peek()方法,除了需要ensureCapacity()外,都執行常數基本操作,因此它們的運行時間也是O(1)。

3.棧的鏈式存儲與實現

棧的鏈式存儲即採用鏈表實現棧。當採用單鏈表存儲線性表後,根據單鏈表的操作特性選擇單鏈表的頭部作爲棧頂,此時,入棧和出棧等操作可以在O(1)時間內完成。

由於棧的操作只在線性表的一端進行,在這裏使用帶頭結點的單鏈表或不帶頭結點的單鏈表都可以。使用帶頭結點的單鏈表時,結點的插入和刪除都在頭結點之後進行;使用不帶頭結點的單鏈表時,結點的插入和刪除都在鏈表的首結點上進行。

下面以不帶頭結點的單鏈表爲例實現棧,如下示意圖所示:

不帶頭結點單鏈表棧示意圖

在上圖中,top爲棧頂結點的引用,始終指向當前棧頂元素所在的結點。若top爲null,則表示空棧。入棧操作是在top所指結點之前插入新的結點,使新結點的next域指向top,top前移即可;出棧則直接讓top後移即可。

如下給出了棧的鏈式存儲實現的代碼:

public class LinkedStack<T> {
    /**
     * 內部結點類
     */
    private class Node{
        private T data;
        private Node next;

        public Node() {
            this(null,null);
        }

        public Node(T data, LinkedStack<T>.Node next) {
            super();
            this.data = data;
            this.next = next;
        }

        public T getData() {
            return data;
        }

        public void setData(T data) {
            this.data = data;
        }

        public Node getNext() {
            return next;
        }

        public void setNext(Node next) {
            this.next = next;
        }
    }

    private Node top; // 首結點,即棧頂
    private int size;// 棧的大小

    public LinkedStack() {
        top = null;
        size = 0;
    }

    public int size(){
        return size;
    }

    public boolean isEmpty(){
        return size == 0;
    }

    public void push(T e){
        Node newNode = new Node(e, top);
        top = newNode;
        size ++;
    }

    public T pop(){
        if(isEmpty()){
            throw new EmptyStackException();
        }
        T data =  top.getData();
        top = top.getNext();
        size --;
        return data;
    }

    public T peek(){
        if(isEmpty()){
            throw new EmptyStackException();
        }
        return top.getData();
    }
}

上述LinkedStack類中有兩個成員變量,其中top表示首結點,也就是棧頂元素所在的結點;size指示棧的大小,即棧中數據元素的個數。不難理解,所有的操作均可以在O(1)時間內完成。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章