數據結構之棧

堆棧

堆棧是一種特殊的線性表,堆棧的數據元素以及數據元素間的邏輯關系和線性表完全相同,其差別是線性表允許在任意位置進行插入和刪除操作,而堆棧只允許在固定一端進行插入和刪除操作。因此也被成為 LIFO(Last In, First Out, 后進先出),

堆棧中允許進行插入和刪除操作的一端稱為棧頂,另一端稱為棧底。堆棧的插入和刪除操作通常稱為進棧或入棧,堆棧的刪除操作通常稱為出?;蛲藯!?/p>

常見的實現方式包括數組實現,容器實現和鏈表實現。

基本算法

  1. 進棧(PUSH)算法
    1. 若 TOP > n 時,則給出溢出信息,作出錯處理(進棧前首先檢查棧是否已滿,滿則溢出;不滿則繼續)
    2. 置 TOP=TOP+1 (棧指針加 1,指向進棧地址)
    3. S(TOP)=X,結束( X 為新進棧的元素)
  2. 退棧(POP)算法
    1. 若TOP < 0,則給出下溢信息,作出錯處理(退棧前先檢查是否已為空棧, 空則下溢;不空則作②)
    2. X=S(TOP),(退棧后的元素賦給X)
    3. TOP=TOP-1,結束(棧指針減1,指向棧頂)

順序棧

順序存儲結構的堆棧稱作順序堆棧。其存儲結構示意圖如下圖所示:

數組實現

public class ArrayStack {

    private int[] stack;
    private int maxSize;
    private int top;

    public ArrayStack(int maxSize) {
        this.maxSize = maxSize;
        stack = new int[maxSize];
        top = -1;
    }

    public void push(int data) {
        if (isFull()) {
            throw new StackOverflowError();
        }

        // 先讓 top + 1 后賦值
        stack[++top] = data;
    }

    public int pop() {
        if (isEmpty()) {
            throw new EmptyStackException();
        }

        // 先返回值,再 top - 1
        return stack[top--];
    }

    public int peek() {
        if (isEmpty()) {
            throw new EmptyStackException();
        }
        return stack[top];
    }

    public boolean isEmpty() {
        return top == -1;
    }

    public boolean isFull() {
        return top == maxSize;
    }
}

容器實現

public class ListStack<T> {

    private List<T> stack;

    public ListStack() {
        stack = new ArrayList<>();
    }

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


    public void push(T data) {
        stack.add(data);
    }

    public T pop() {
        if (isEmpty()) {
            throw new EmptyStackException();
        }

        return stack.remove(stack.size() - 1);
    }

    public T peek() {
        if (isEmpty()) {
            throw new EmptyStackException();
        }

        return stack.get(stack.size() - 1);
    }
    
}

鏈式堆棧

鏈式存儲結構的堆棧稱作鏈式堆棧。

與單鏈表相同,鏈式堆棧也是由一個個結點組成的,每個結點由兩個域組成,一個是存放數據元素的數據元素域 data,另一個是存放指向下一個結點的對象引用(即指針)域 next。

堆棧有兩端,插入數據元素和刪除數據元素的一端為棧頂,另一端為棧底。鏈式堆棧都設計成把靠近堆棧頭head的一端定義為棧頂。

依次向鏈式堆棧入棧數據元素a0, a1, a2, ..., an-1后,鏈式堆棧的示意圖如下圖所示:

public class LinkedStack<T> {

    class Node {
        T data;
        Node next;

        public Node() {
        }

        public Node(T data) {
            this.data = data;
        }

        public Node(T data, Node next) {
            this.data = data;
            this.next = next;
        }
    }

    private Node top;

    public void push(T data) {
        top = new Node(data, top);
    }

    public T pop() {
        if (isEmpty()) {
            throw new EmptyStackException();
        }

        T data = top.data;
        top = top.next;
        return data;
    }

    public T peek() {
        if (isEmpty()) {
            throw new EmptyStackException();
        }

        return top.data;
    }

    public boolean isEmpty() {
        return null == top;
    }
}

Java 中棧與堆的區別

棧(stack):(線程私有)

是一個先進后出的數據結構,通常用于保存方法(函數)中的參數,局部變量。在java中,所有基本類型和引用類型的引用都在棧中存儲。棧中數據的生存空間一般在當前scopes內(就是由{...}括起來的區域)。

堆(heap):(線程共享)

是一個可動態申請的內存空間(其記錄空閑內存空間的鏈表由操作系統維護),C中的malloc語句所產生的內存空間就在堆中。在java中,所有使用new xxx()構造出來的對象都在堆中存儲,當垃圾回收器檢測到某對象未被引用,則自動銷毀該對象。所以,理論上說java中對象的生存空間是沒有限制的,只要有引用類型指向它,則它就可以在任意地方被使用。

hashCode 與對象之間的關系

如果兩個對象的hashCode不相同,那么這兩個對象肯定也不同。

如果兩個對象的hashCode相同,那么這兩個對象有可能相同,也有可能不同。

總結一句:不同的對象可能會有相同的hashCode;但是如果hashCode不同,那肯定不是同一個對象。

public class StringTest {

    public static void main(String[] args) {

        //s1 和 s2 其實是同一個對象。對象的引用存放在棧中,對象存放在方法區的字符串常量池
        String s1 = "china";
        String s2 = "china";

        //凡是用new關鍵創建的對象,都是在堆內存中分配空間。
        String s3 = new String("china");

        //凡是new出來的對象,絕對是不同的兩個對象。
        String s4 = new String("china");

        System.out.println(s1 == s2);  //true
        System.out.println(s1 == s3);
        System.out.println(s3 == s4);
        System.out.println(s3.equals(s4));

        System.out.println("\n-----------------\n");
      /*String很特殊,重寫從父類繼承過來的hashCode方法,使得兩個
       *如果字符串里面的內容相等,那么hashCode也相等。
       **/

        //hashCode相同
        System.out.println(s3.hashCode());  //hashCode為94631255
        System.out.println(s4.hashCode());  //hashCode為94631255

        //identityHashCode方法用于獲取原始的hashCode
        //如果原始的hashCode不同,表明確實是不同的對象

        //原始hashCode不同
        System.out.println(System.identityHashCode(s3)); //2104928456
        System.out.println(System.identityHashCode(s4)); //2034442961

        System.out.println("\n-----------------\n");

        //hashCode相同
        System.out.println(s1.hashCode());  //94631255
        System.out.println(s2.hashCode());  //94631255

        //原始hashCode相同:表明確實是同一個對象
        System.out.println(System.identityHashCode(s1));  //648217993
        System.out.println(System.identityHashCode(s2));  //648217993
    }
}

上面的代碼中,注釋已經標明了運行的結果。通過運行結果我們可以看到,s3和s4的字符串內容相同,但他們是兩個不同的對象,由于String類重寫了hashCode方法,他們的hashCode相同,但原始的hashCode是不同的。

參考

數據結構Java實現05----棧:順序棧和鏈式堆棧

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容