java棧和隊列



    可變長數組實現
    鏈表實現
    數組與鏈表的對比
隊列
    鏈表實現

下壓棧(簡稱棧)是一種基於后進后出(LIFO)策略的集合類型。這里學習分別用數組和鏈表這兩種基礎數據結構來實現棧。
棧支持的基本操作有push,pop。

可變長數組實現

要用數組實現棧,可以聲明一個int型的標記,這個標記指向的位置即為棧頂,push操作時,將值放在數據中標記的位置,同時將標記+1,pop時,返回數組在標記位置的值,同時標記-1。
但java中的數組在聲明的時候其長度就已經固定了,所以棧使用的空間只能是這個數組最大容量的一部分。為了能容納更多的數據而聲明一個特別大的數組會非常浪費空間,那如何解決這個問題,達到既不會浪費數組空間也不會超出數組范圍呢?
可以采用動態調整數組大小的方式,在push操作導致棧已滿時,重新創建一個數組,其容量為原數組的兩倍。同理在pop操作使數組的閑置空間達到一定程度時,重新創建一個容量更小的數組。但閑置的判斷標准不能為一半1/2,否則會造成在1/2的臨界點處push和pop操作時發生“抖動”,即頻繁地進行數組擴容、縮容操作,這會極大地降低棧的性能。所以通常的做法是在減少到數組容量的1/4時縮容為1/2。

實現可變長數組的代碼如下:

public class StackResizeArray<Item>  {
    private Item[] a; // array of items
    private int n; // number of elements on stack

    public StackResizeArray() {
        a = (Item[]) new Object[2];
        n = 0;
    }

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

    public int size() {
        return n;
    }

    private void resize(int capacity) {
        assert capacity >= n;

        Item[] temp = (Item[]) new Object[capacity];
        for (int i = 0; i < n; i++) {
            temp[i] = a[i];
        }
        a = temp;
    }

    public void push(Item item) {
        if (n == a.length)
            resize(2 * a.length); // double size of array if necessary
        a[n++] = item; // add item
    }

    public Item pop() {
        if (isEmpty())
            throw new NoSuchElementException("Stack underflow");
        Item item = a[n - 1];
        a[n - 1] = null; // to avoid loitering
        n--;
        // shrink size of array if necessary
        if (n > 0 && n == a.length / 4)
            resize(a.length / 2);
        return item;
    }
}

pop方法中,a[n - 1] = null將數組中已經出棧的位置設為null,是為了避免對象游離,否則這個位置的對象雖然已經不在棧中,但還被數組引用着,導致GC無法對其回收。

鏈表實現

棧的另外一種實現方式是采用鏈表,鏈表是一種遞歸的數據結構,它或者為空,或者指向一個結點的引用,該結點含有一個泛型的元素和一個指向另一條鏈表的引用。 構成鏈表的基本結點可以為:

private static class Node<Item> {
    public Item item;
    public Node next;
}

其中泛型的item變量存放數據,同樣是Node類型的next變量用來指向下一個結點。將鏈表的頭部作為棧頂,push相當於在表頭插入元素,pop是從表頭刪除元素。 代碼如下:

public class StackLinkedList<Item> {
    private static class Node<Item> {
        public Item item;
        public Node next;
    }

    private Node<Item> first;
    private int N;

    private boolean isEmpty() {
        return first == null;
    }

    public int size() {
        return N;
    }

    public void push(Item item) {
        Node<Item> oldFirst = first;
        first = new Node<Item>();
        first.item = item;
        first.next = oldFirst;
        N++;
    }

    public Item pop() {
        if (isEmpty())
            throw new NoSuchElementException("Stack underflow");
        Item item = first.item;
        first = first.next;
        N--;
        return item;
    }
}
數組與鏈表的對比

數組與鏈表的區別決定了兩種棧實現的區別:

  • 存取方式上,數組可以順序存取或者隨機存取,而鏈表只能順序存取; 
  • 存儲位置上,數組邏輯上相鄰的元素在物理存儲位置上也相鄰,而鏈表不一定; 
  • 存儲空間上,鏈表由於帶有指針域,存儲密度不如數組大; 
  • 按序號查找時,數組可以隨機訪問,需要的時間是常數,而鏈表不支持隨機訪問,需要的時間與數據規模成線性關系。
  • 按值查找時,若數組無序,數組和鏈表時間復雜度均為O(n),但是當數組有序時,可以采用折半查找將時間復雜度降為O(logn);
  • 插入和刪除時,數組平均需要移動n/2個元素,而鏈表只需修改指針即可; 
  • 空間分配方面: 雖然可變長數組可以擴充,但需要移動大量元素,導致操作效率降低,而且如果內存中沒有更大塊連續存儲空間將導致分配失敗; 鏈表存儲的節點空間只在需要的時候申請分配,只要內存中有空間就可以分配,操作比較靈活高效;

隊列

鏈表實現

先進先出隊列(簡稱隊列)是一種基於先進先出(FIFO)策略的集合類型。這里只關注隊列的鏈表實現。與鏈表實現的棧類似,用結點包含泛型的item變量和next變量,前者存放數據,后者用來指向下一個結點。不同之處在於鏈表的頭部、尾部都會被操作,從鏈表的一端入隊(enqueue),從另一端出隊(dequeue) 代碼:

public class Queue<Item> {
    private static class Node<Item> {
        public Item item;
        public Node next;
    }

    private Node<Item> first;
    private Node<Item> last;
    private int N;

    public void enqueue(Item item) {
        Node<Item> oldLast = last;
        last = new Node<Item>();
        last.item = item;
        if (isEmpty()) {
            first = last;
        } else {
            oldLast.next = last;
        }
        N++;
    }

    public Item dequeue() {
        if (isEmpty()) {
            return null;
        }
        Item result = first.item;
        first = first.next;
        if (isEmpty()) {
            last = null;
        }
        N--;
        return result;
    }

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

    public int size() {
        return N;
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM