1. 程式人生 > >Java的順序棧和鏈式棧

Java的順序棧和鏈式棧

urn implement public 可能 object 指定大小 常數 額外 異常

棧的定義

棧是限制在表的一段進行插入和刪除的運算的線性表,通常能夠將插入、刪除的一端為棧頂,例外一端稱為棧底,當表中沒有任何元素的時候稱為空棧。

通常刪除(又稱“退棧”)叫做彈出pop操作,每次刪除的都是棧頂最新的元素;每次插入(又稱“進棧”)稱為壓入push操作。

當棧滿的時候,進行push 操作,會上溢,當空棧的時候進行退棧操作的時稱為下溢

上溢是一種出錯的情況,下溢可能是正常的情況處理。

堆棧的運算是按照後進先出的原則,簡稱LIFO

棧的基本運算定義:

  • initStack:構造一個空棧;
  • stackEmpty:判斷是否為空;
  • stackFUll:判斷是否棧滿;
  • push:進棧,將該元素壓入棧頂;
  • pop:退棧,彈出棧頂元素,註意非空判斷;
  • stackTop:去除棧頂元素,不改變指針。

做個簡單的Stack接口:

package com.wuwii.utils;

/**
 * 堆棧
 * @author Zhang Kai
 * @version 1.0
 * @since <pre>2017/12/14 22:51</pre>
 */
public interface Stack<E> {
    /**
     * 進棧
     *
     * @param element 進棧的元素
     */
    void push(E element);

    /**
* 彈出棧頂元素 ,並改變指針 * * @return 棧頂元素 */ E pop(); /** * 返回棧頂元素 ,不改變指針 * * @return 棧頂元素 */ E topElement(); /** * 判斷是否為空棧 * * @return true為空棧 */ Boolean isEmpty(); /** * 清空棧 */ void clear(); }

順序棧

就是符合LIFO運算規則的順序線性表。

package com.wuwii.utils;

/**
 * 順序棧
 * @author Zhang Kai
 * @version 1.0
 * @since <pre>2017/12/14 23:05</pre>
 */
public class ArrayStack<E> implements Stack<E> {
    /**
     * 初始化棧的默認大小
     */
    private final int defaultSize = 10;
    /**
     * 棧的集合大小
     */
    private int size;
    /**
     * 棧頂的位置
     */
    private int top;
    /**
     * 元素存儲在數組
     */
    private Object[] elements;

    /**
     * 初始化默認大小為10 的棧
     */
    public ArrayStack() {
        initStack(defaultSize);
    }

    /**
     * 初始化指定大小的棧
     * @param givenSize 指定棧大小
     */
    public ArrayStack(Integer givenSize) {
        initStack(givenSize);
    }

    /**
     * 初始化棧
     * @param givenSize 給定的棧大小
     */
    private void initStack(Integer givenSize) {
        size = givenSize;
        top = 0;
        elements = new Object[size];
    }

    /**
     * 清空棧
     */
    @Override
    public void clear() {
        top = 0;
    }

    /**
     * 進棧
     * @param element 進棧的元素
     */
    @Override
    public void push(E element) {
        sizeCheckForPush();
        elements[top++] = element;
    }

    /**
     * 彈出棧頂元素 ,並改變指針
     * @return 棧頂元素
     */
    @Override
    public E pop() {
        sizeCheckForPop();
        return (E) elements[--top];
    }
    /**
     * 返回棧頂元素 ,不改變指針
     * @return 棧頂元素
     */
    @Override
    public E topElement() {
        sizeCheckForPush();
        return (E) elements[top - 1];
    }

    /**
     * 判斷是否為空棧
     * @return true為空棧
     */
    @Override
    public Boolean isEmpty() {
        return size == 0;
    }
    
    /**
     * 在進棧的時候檢查
     */
    private void sizeCheckForPush() {
        if (top >= size) {
            throw new RuntimeException("Stack overflow");
        }
    }

    /**
     * 退棧檢查
     */
    private void sizeCheckForPop() {
        if (isEmpty()) {
            throw new RuntimeException("Stack is empty");
        }
    }
}

鏈式棧

符合LIFO運算規則的鏈式線性表。

package com.wuwii.utils;


/**
 * @author Zhang Kai
 * @version 1.0
 * @since <pre>2017/12/15 12:58</pre>
 */
public class LinkStack<E> implements Stack<E> {
    /**
     * 鏈式單元
     */
    private Node<E> top;

    /**
     * 初始化鏈式堆棧
     */
    public LinkStack() {
        initStack();
    }

    /**
     * 初始化
     */
    private void initStack() {
        top = null;
    }

    /**
     * 存儲單元
     */
    private static class Node<E> {
        E element;
        Node<E> next;
        Node(E element, Node<E> next) {
            this.element = element;
            this.next = next;
        }
    }
    /**
     * 進棧
     *
     * @param element 進棧的元素
     */
    @Override
    public void push(E element) {
        top = new Node<E>(element, top);
    }

    /**
     * 彈出棧頂元素 ,並改變指針
     *
     * @return 棧頂元素
     */
    @Override
    public E pop() {
        checkEmpty();
        E element = top.element;
        top = top.next;
        return element;
    }

    /**
     * 返回棧頂元素 ,不改變指針
     *
     * @return 棧頂元素
     */
    @Override
    public E topElement() {
        checkEmpty();
        return top.element;
    }

    /**
     * 判斷是否為空棧
     *
     * @return true為空棧
     */
    @Override
    public Boolean isEmpty() {
        return top == null;
    }

    /**
     * 清空棧
     */
    @Override
    public void clear() {
        if (isEmpty()) {
            return;
        }
        for (Node<E> x = top; x != null; ) {
            Node<E> next = x.next;
            x.element = null;
            x.next = null;
            x = next;
        }
        size = 0;
    }

    /**
     * 檢查鏈式堆棧是否為空,為空拋出異常
     */
    private void checkEmpty() {
        if (isEmpty()) {
            throw new RuntimeException("LinkStack is empty");
        }
    }
}

首先push 修改新產生的鏈表節點的next 域並指向棧頂,然後設置top 指向新的鏈表節點,pop則相反。

順序棧和鏈式棧的比較

實現鏈式棧和順序棧的操作都是需要常數時間,時間復雜度為O(1),主要從空間和時間復雜度考慮。

順序棧初始化的時候必須要給定指定大小,當堆棧不滿的時候,會造成一部分的空間浪費,鏈式棧變長,相對節約空間,但是增加了指針域,額外加大了數據結構的開銷。

當需要多個堆棧共享的時候,順序存儲中可以充分的利用順序棧的單向延伸,將一個數組可以存在兩個堆棧裏,每個堆棧從各自的棧頂出發延伸,這樣減少了空間的浪費。但只有兩個為堆棧的空間有相反的需求的時候才能使用。就是最好一個堆棧只能增加,一個只能減少。如果,兩個一起增加,可能造成堆棧的溢出。

如果在多個順序堆棧共享空間,一個堆棧滿了,其他可能沒滿,需要使用堆棧的LIFO 運算法則,將滿的堆棧元素向左或者右進行平移操作,這樣會造成大量的數據元素移動,使得時間的開銷增大。

相對來說,使用兩個堆棧共享一個空間是比較適宜的存儲方式,但是也增加了堆棧溢出的危險。

由於鏈式存儲結構的不連續性,什麽時候需要,就什麽時候去存儲,不存在溢出的問題,但是增加了結構的開銷,總體上來說浪費了空間,但是不需要堆棧共享,

Java的順序棧和鏈式棧