順序棧與鏈式棧的圖解與實現

# 順序棧與鏈式棧的圖解與實現java

  • 棧是一種特殊的線性表,它與線性表的區別體如今增刪操做上
  • 棧的特色是先進後出,後進先出,也就是說棧的數據操做只能發生在末端,而不容許在中間節點進行操做

  • 如上圖所示,對棧的增刪操做都只能在末端也就是棧頂操做,
  • 棧既然是線性表那麼就存在表頭和表尾,不過在棧結構中,對其都進行限制改造,表尾用來輸入數據也叫作棧頂(top),相應的 表頭就是棧底(bottom),棧頂和棧頂是兩個指針用來表示這個棧
  • 與線性表相似,棧也是又順序表示和鏈式表示,分別稱做順序棧和鏈棧

棧的基本操做

  • 如何經過棧這個後進先出的線性表,來實現增刪查呢?
  • 初始時,棧內沒有數據,即空棧。此時棧頂就是棧底。
  • 當存入數據時,最早放入的數據會進入棧底。接着加入的數據都會放入到棧頂的位置。
  • 若是要刪除數據,也只能經過訪問棧頂的數據並刪除。對於棧的新增操做,一般也叫做 push 或壓棧。
  • 對於棧的刪除操做,一般也叫做 pop或出棧。對於壓棧和出棧,咱們分別基於順序棧和鏈棧來分析

順序棧

  • 順序棧即就是順序存儲元素的,一般順序棧咱們能夠經過數組來實現,將數組的首元素放在棧底,最後一個元素放在棧頂,以後指定一個 top 指針指向棧頂元素的位置
  • 當棧中只有一個元素是,此時 top=0 ,通常以 top 是否爲 -1 來斷定是否爲空棧,當定義了棧的最大容量時,則棧頂 top 必須小於最大容量值
  • 下面咱們經過 Java 代碼實現一個順序棧,很是簡單以下:
/**
 * @url: i-code.online
 * @author: 雲棲簡碼
 * @time: 2020/12/8 16:48
 */
public class Stack<T> {

    private Object[] stack;
    private int stackSize;
    private int top = -1;

    public Stack(int size){
        stackSize = size;
        stack = new Object[size];
    }

    public void push(T value){
        if (top < stackSize-1){
            top++;
            stack[top] = value;
            return;
        }
        throw new ArrayIndexOutOfBoundsException(top +"越界");
    }

    public T pop(){
        if (top > -1){
            top--;
           return (T) stack[top+1];
        }
        throw new ArrayIndexOutOfBoundsException(top +"越界");
    }

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

}
  • 當須要新增數據元素,即入棧操做時,就須要將新插入元素放在棧頂,並將棧頂指針增長 1。以下圖所示:
    數據結構 (1).png
  • 刪除數據元素,即出棧操做,只須要 top-1 就能夠了。

對於查找操做,棧沒有額外的改變,跟線性表同樣,它也須要遍歷整個棧來完成基於某些條件的數值查找,上述代碼中並未去實現該功能node

鏈棧

  • 關於鏈式棧,就是用鏈表的方式對棧的表示。一般,能夠把棧頂放在單鏈表的頭部,以下圖所示。因爲鏈棧的後進先出,原來的頭指針就顯得毫無做用了。所以,對於鏈棧來講,是不須要頭指針的。相反,它須要增長指向棧頂的 top 指針,這是壓棧和出棧操做的重要支持。

數據結構 (2).png

  • 對於鏈表咱們添加都是在其後追加,可是對於鏈棧,新增數據的壓棧操做須要額外處理的,就是棧的 top 指針。以下圖所示,插入新的數據放在頭部,則須要讓新的結點指向原棧頂,即 top 指針指向的對象,再讓 top 指針指向新的結點。

數據結構 (3).png

  • 在鏈式棧中進行刪除操做時,只能在棧頂進行操做。所以,將棧頂的 top 指針指向棧頂元素的 next 指針便可完成刪除。對於鏈式棧來講,新增刪除數據元素沒有任何循環操做,其時間複雜度均爲 O(1)
  • 經過代碼簡單實現棧的操做,以下:
/**
 * @url: i-code.online
 * @author: 雲棲簡碼
 * @time: 2020/12/8 20:57
 */
public class LinkedList<E> {


    private Node<E> top = new Node<>(null,null);


    public void push(E e){
        Node<E> node = new Node<>(e,top.next);
        top.next = node;
    }

    public E pop(){
        if (top.next == null){
            throw new NoSuchElementException();
        }
        final Node<E> next = top.next;
        top.next = next.next;
        return next.item;
    }



    private static class Node<E>{
        E item;
        Node<E> next;

        public Node(E item, Node<E> next){
            this.item = item;
            this.next = next;
        }
    }

}

對於查找操做,相對鏈表而言,鏈棧沒有額外的改變,它也須要遍歷整個棧來完成基於某些條件的數值查找。面試

  • 無論是順序棧仍是鏈棧,數據的新增、刪除、查找與線性表的操做原理極爲類似,時間複雜度徹底同樣,都依賴當前位置的指針來進行數據對象的操做。區別僅僅在於新增和刪除的對象,只能是棧頂的數據結點。

棧的案例

  • 咱們能夠經過一個案例來看棧的具體使用,這裏選取 leetcode 上的案例來練習,以下

有效括號

  • 給定一個只包括 '(',')','{','}','[',']' 的字符串,判斷字符串是否有效。有效字符串需知足:左括號必須與相同類型的右括號匹配,左括號必須以正確的順序匹配。例如,{ [ ( ) ( ) ] } 是合法的,而{ ( [ ) ] }是非法的。
  • 這個問題很適合採用棧來處理。緣由是,在匹配括號是否合法時,左括號是從左到右依次出現,而右括號則須要按照「後進先出」的順序依次與左括號匹配。所以,實現方案就是經過棧的進出來完成。
  • 具體的實現思路,咱們能夠遍歷字符串從左起,當遇到左括號時進行壓榨操做,而到遇到右括號時則繼續出棧,判斷出棧的括號是否與當前的右括號是一對,若是不是則非法,若是一致則繼續遍歷直到結束
  • 代碼以下:
public boolean isValid(String s) {
        Stack stack = new Stack();
        for(int i =0;i<s.length();i++){
            char curr = s.charAt(i);
            if (isLeft(curr)) {
                stack.push(curr);
            }else {
                if (stack.empty())
                    return false;
                if (!isPair(curr,(char)stack.pop())){
                    return false;
                }
            }
        }
        if (stack.empty()){
            return true;
        }else {
            return false;
        }

    }

    public boolean isPair(char curr,char expt){
        if ((expt == '[' && curr == ']') || (expt == '{' && curr == '}') || (expt == '(' && curr == ')'))
            return true;
        return false;
    }

    public boolean isLeft(char c){
        if (c == '{' || c == '[' || c == '(')
            return true;
        return false;
    }

總結

  • 棧繼承了線性表特性,是一個特殊的線性表
  • 棧只容許數據從棧頂進出,即棧的特性先進後出
  • 無論是順序棧仍是鏈式棧,它們對於數據的新增操做和刪除操做的時間複雜度都是 O(1)。而在查找操做中,棧和線性表同樣只能經過全局遍歷的方式進行,也就是須要 O(n) 的時間複雜度
  • 當咱們面臨頻繁增刪節點,同時數據順序有後來居上的特色時棧就是個不錯的選擇。例如,瀏覽器的前進和後退,括號匹配等問題

推薦閱讀



本文由AnonyStar 發佈,可轉載但需聲明原文出處。
歡迎關注微信公帳號 :雲棲簡碼 獲取更多優質文章
更多文章關注筆者博客 :雲棲簡碼 i-code.online編程

相關文章
相關標籤/搜索