1. 程式人生 > >棧(stack)原理

棧(stack)原理

    一.原理
 棧(stack)又名堆疊,它是一種運算受限的線性表。其限制是僅允許在表的一端進行插入和刪除運算。這一端被稱為棧頂,相對地,把另一端稱為棧底。向一個棧插入新元素又稱作進棧、入棧或壓棧,它是把新元素放到棧頂元素的上面,使之成為新的棧頂元素;從一個棧刪除元素又稱作出棧或退棧,它是把棧頂元素刪除掉,使其相鄰的元素成為新的棧頂元素。
     二.特點
棧的基本特點:先入後出,後入先出。除頭尾節點之外,每個元素有一個前驅,一個後繼。

java中的Stack

  Stack 類表示後進先出(LIFO)的物件堆疊。它通過五個操作對類 Vector 進行了擴充套件 ,允許將向量視為堆疊。它提供了通常的 push 和 pop 操作,以及取堆疊頂點的 peek 方法、測試堆疊是否為空的 empty 方法、在堆疊中查詢項並確定到堆疊頂距離的 search 方法。首次建立堆疊時,它不包含項。

Deque 介面及其實現提供了 LIFO 堆疊操作的更完整和更一致的 set,應該優先使用此 set,而非此類。例如:


Deque<Integer> stack = new ArrayDeque<Integer>(); 
 

成員方法:

E push(E item) 
把項壓入堆疊頂部。  

E pop() 
移除堆疊頂部的物件,並作為此函式的值返回該物件。  

E peek() 
檢視堆疊頂部的物件,但不從堆疊中移除它。  

boolean empty() 
測試堆疊是否為空。  

int search(Object o) 
返回物件在堆疊中的位置,以 1 為基數。 

Stack繼承於Vector,Vector本身是一個可增長的物件陣列。 
Stack並不要求其中儲存資料的唯一性,當Stack中有多個相同的item時,呼叫search方法,只返回與查詢物件equal並且離棧頂最近的item與棧頂間距離。

empty()

  判斷stack是否為空,就需要有一個變數來計算當前棧的長度,如果該變數為0,則表示該棧為空。

原始碼:

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

size()方法在父類Vector中實現了,在Vector裡面有一個變數elementCount來表示容器裡元素的個數。如果為0,則表示容器空。

public synchronized int size() {  
    return elementCount;  
}  

peek()

  返回棧頂端的元素,如果棧為空的話,則要丟擲異常。

public synchronized E peek
() { int len = size(); if (len == 0) throw new EmptyStackException(); return elementAt(len - 1); }

elementAt方法也是在Vector裡面實現的,實際上是用一個elementData的Object陣列來儲存元素的。

public synchronized E elementAt(int index) {  
    if (index >= elementCount) {  
        throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);  
    }  

    return elementData(index);  
}  

@SuppressWarnings("unchecked")  
E elementData(int index) {  
    return (E) elementData[index];  
} 

peek()

  將棧頂的元素彈出來,如果棧裡有元素,就取最頂端的那個,否則就要丟擲異常。

public synchronized E pop() {  
     E   obj;  
     int  len = size();  

     obj = peek();  
     removeElementAt(len - 1);  

     return obj;  
} 

  通過peek()取到頂端的元素之後,我們需要用removeElementAt()方法將最頂端的元素移除。 
removeElementAt()方法定義在vector中。

public synchronized void removeElementAt(int index) {  
    modCount++;  
    if (index >= elementCount) {  
        throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);  
        }  
    else if (index < 0) {  
        throw new ArrayIndexOutOfBoundsException(index);  
    }  
    int j = elementCount - index - 1;  
    if (j > 0) {  
        System.arraycopy(elementData, index + 1, elementData, index, j);  
    }  
    elementCount--;  
    elementData[elementCount] = null; /* to let gc do its work */  
}  

  這裡用待刪除元素的後面元素依次覆蓋前面一個元素。這樣,就相當於將陣列的實際元素長度給縮短了。

push()

  將資料入棧

public E push(E item) {  
    addElement(item);  

    return item;  
}

  將要入棧的元素放到陣列的末尾,再將陣列長度加1就可以了。addElement()方法也在vector中(好父親啊)。

public synchronized void addElement(E obj) {  
    modCount++;  
    ensureCapacityHelper(elementCount + 1);  
    elementData[elementCount++] = obj;  
}  

private void ensureCapacityHelper(int minCapacity) {  
    // overflow-conscious code  
    if (minCapacity - elementData.length > 0)  
        grow(minCapacity);  
}  

private void grow(int minCapacity) {  
    // overflow-conscious code  
    int oldCapacity = elementData.length;  
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ?  
                                    capacityIncrement : oldCapacity);  
    if (newCapacity - minCapacity < 0)  
        newCapacity = minCapacity;  
    if (newCapacity - MAX_ARRAY_SIZE > 0)  
        newCapacity = hugeCapacity(minCapacity);  
    elementData = Arrays.copyOf(elementData, newCapacity);  
}  

private static int hugeCapacity(int minCapacity) {  
    if (minCapacity < 0) // overflow  
        throw new OutOfMemoryError();  
    return (minCapacity > MAX_ARRAY_SIZE) ?  
        Integer.MAX_VALUE :  
        MAX_ARRAY_SIZE;  
} 

search()

  找到一個最靠近棧頂端的匹配元素,然後返回這個元素到棧頂的距離

public synchronized int search(Object o) {  
    int i = lastIndexOf(o);  

    if (i >= 0) {  
        return size() - i;  
    }  
    return -1;  
} 

對應在vector裡面的實現也相對容易理解:

public synchronized int lastIndexOf(Object o) {  
    return lastIndexOf(o, elementCount-1);  
}  

public synchronized int lastIndexOf(Object o, int index) {  
    if (index >= elementCount)  
        throw new IndexOutOfBoundsException(index + " >= "+ elementCount);  

    if (o == null) {  
        for (int i = index; i >= 0; i--)  
            if (elementData[i]==null)  
                return i;  
    } else {  
        for (int i = index; i >= 0; i--)  
            if (o.equals(elementData[i]))  
                return i;  
    }  
    return -1;  
}  

lastIndexOf是從陣列的末端往前遍歷,如果找到這個物件就返回。如果到頭了,還未找到就返回個-1。

棧和佇列的區別

  • 佇列是FIFO的(先進先出),堆疊是FILO的(現今後出)

  • 棧是限定只能在表的一端進行插入和刪除操作的線性表。 佇列是限定只能在表的一端進行插入和在另一端進行刪除操作的線性表

  • 棧只能從頭部取資料,也就最先放入的需要遍歷整個棧最後才能取出來,而且在遍歷資料的時候還得為資料開闢臨時空間; 
    佇列基於地址指標進行遍歷,而且可以從頭或尾部開始遍歷,但不能同時遍歷,無需開闢臨時空間,因為在遍歷的過程中不影像資料結構,速度要快的多。