動畫:動畫從零學編程之 “棧” 你掌握這些必備了嗎?

在這裏插入圖片描述作者 | 小鹿
來源 | 小鹿動畫學編程


寫在前邊

對於棧的認識,相信每個學習數據結構的小夥伴多多少少有一定的認識和了解。很多剛剛學習的小夥伴說學習數據結構在實際中沒怎麼見到應用,那是因爲你沒有去仔細的觀察,而且像棧這常用到的數據結構通常會使用在實際開發中,比如:表達式的運算、花括號的匹配以及瀏覽器的前進後退等等很多。

這些實際開發的實現如果不去研究,你永遠不知道數據結構在實際中的應用,當你學習完今天的棧數據結構時,然後去研究下實際中已經使用到的應用,才能讓你在今後的實際開發中用到棧這種數據結構,從而使你的開發更加靈活、多變。


思維導圖

這篇文章將要學習到的內容。

在這裏插入圖片描述

1、基本常識

1.1 什麼是棧

我們用一種最簡單的生活常識描述一下,比如我們往櫃子裏放東西,先放的東西是需要放到櫃子最裏邊,後放的東西在櫃子的最外邊;如果我們要取東西,先要取櫃子最外邊的東西,才能取到櫃子最裏邊的東西。這種先進後出,後進先出的結構稱爲“棧”。
在這裏插入圖片描述

1.2 棧的特點

“先進後出,後進先出”。

1.3 棧的操作

棧的操作就兩種,分別爲出棧和入棧。那我們上邊的例子,我們往櫃子裏放東西的過程稱爲入棧;我們在櫃子裏拿東西的過程稱爲出棧
在這裏插入圖片描述

PS:櫃子只有一個出口和入口,而且出口和入口是一樣的。如果兩端都有開口,就變成了隊列,後期的文章會講到。

2、棧的實現

我們前邊的文章也講過,所有的數據結構基本都是由數組和鏈表演化而來的,所以今天講的棧這種數據結構也不例外。

棧的實現主要有兩種,一種是數組的實現,叫做順序棧,另外一種是鏈表的實現,叫做鏈式棧。如下:

2.1 順序棧

在這裏插入圖片描述

2.2 鏈表棧

在這裏插入圖片描述

2.3 代碼實現

順序棧

/**
 * 功能:基於數組的順序棧
 * 公衆號:一個不甘平凡的碼農
 * @author:小鹿
 *
 */
public class ArrayStack {
	
   private String[] items;  // 數組
   private int count;       // 棧中元素個數
   private int n;           // 棧的大小

   // 初始化數組,申請一個大小爲 n 的數組空間
   public ArrayStack(int n) {
     this.items = new String[n];
     this.n = n;
     this.count = 0;
   }
 
   /**
    * 功能:入棧
    * 說明:數組入棧的入口爲數組尾部
    * @param item :入棧數據元素
    * @return:是否入棧成功
    */
   public boolean push(String item) {
     // 數組空間不夠了,直接返回 false,入棧失敗。
     if (count == n) return false;
     // 將 item 放到下標爲 count 的位置
     items[count] = item;
     //數組長度+1
     ++count;
     //入棧成功
     return true;
   }
  
   /**
    * 功能:出棧
    * 
    * @return:返回出棧元素
    */
   public String pop() {
     // 棧爲空,則直接返回 null
     if (count == 0) return null;
     // 返回下標爲 count-1 的數組元素
     String tmp = items[count-1];
     //數組長度-1
     --count;
     //返回出棧數據元素
     return tmp;
   }
}

鏈式棧


/**
 * 功能:基本鏈表的鏈式棧,入棧、出棧、輸出棧
 * @author : 小鹿
 * 公衆號:一個不甘平凡的碼農
 */
public class StackBasedLinkedList {
	//定義棧頂指針
	private Node top = null;
	
	//定義棧結點
	private static class Node {
		//棧結點數據域
	    private int data;
	    //棧結點指針域
	    private Node next;
	    //構造函數
	    public Node(int data, Node next) {
	      this.data = data;
	      this.next = next;
	    }
	    //get 獲取數據域方法
	    public int getData() {
	      return data;
	    }
	}
	
	/**
	 * 功能:入棧
	 * @param value:要入棧的數據元素
	 */
	public void push(int value) {
		//創建一個棧結點 
	    Node newNode = new Node(value, null);
	    // 判斷棧是否爲空
	    if (top == null) {
	      //如果棧爲空,就將入棧的值作爲棧的第一個元素
	      top = newNode;
	    } else {
	      //否則插入到top棧結點前(所謂的就是單鏈表的頭插法)
	      newNode.next = top;
	      top = newNode;
	    }
	}
	
	/**
	 * 功能 : 出棧
	 * @return: -1 爲棧中沒有數據
	 */
	public int pop() {
		// 如果棧的最頂層棧結點爲null,棧爲空
	    if (top == null) return -1;
	    
	    //否則執行出棧操作,現將棧頂結點的數據元素賦值給 Value
	    int value = top.data;
	    //將 top 指針向下移動
	    top = top.next;
	    //返回出棧的值
	    return value;
	}	
	
	/**
	 * 功能:輸出棧中所有元素
	 */
	public void printAll() {
		//將棧頂指針賦值給p
	    Node p = top;
	    //循環遍歷棧(遍歷單鏈表)
	    while (p != null) {
	      System.out.print(p.data + " ");
	      //指向下一個結點
	      p = p.next;
	    }
	    System.out.println();
	}
}

3、棧的性能

我們從上邊學到了棧的基本結構和特點,還有棧的基本操作。如果我們學習一種數據結構,主要分析它的性能如何。還記得怎麼分析數據結構性能嗎?主要從兩方面入手,第一,時間效率(時間複雜度);第二,空間上的消耗(空間複雜度)。我們就從以上兩個方面分析一下棧這種數據結構的性能。


3.1 時間複雜度

時間上的消耗主要分析棧的操作所消耗的時間,我們共兩種操作,入棧和出棧,其實在數組中中,我們操作尾部的數據就相當於入棧和出棧,直接根據下標取得相應的元素就好(JS 中數組的 pop 和 push 方法),所以時間複雜度是 O(1)。


3.2 空間複雜度

空間複雜度的判斷是所需要開闢的臨時空間,順序棧和鏈式棧只需要大小爲 n 的空間就可以,入棧和出棧需要一個臨時空間來存儲變量,空間複雜度爲 O(1)。


3.3 棧的動態擴容

大家有沒有想過這樣一種情況,如果棧滿的時候,再進行入棧操作,棧內就放不下了,我們需要動態擴容。主要是順序棧的動態擴容比較麻煩,和我麼你之前的數組的文章動態擴容一樣的,對於動態擴容的性能,可以自己嘗試一下。可以根據之前的文章來分析《佩奇學編程 | 複雜度分析原來這麼簡單》。


4、棧的實際應用

既然我們把棧的性能分析透了,理解透了,那麼我們看看棧在實際中有哪些應用吧。


4.1 應用一 :棧在函數中的應用

函數我們每個人再熟悉不過了,你是不是很納悶,棧怎麼會在函數中能夠應用的到呢,我學了這幾年函數,我咋不知道函數中還有棧的操作。

加入我們程序開始執行代碼,執行到我們聲明的函數時,計算機內部會發生什麼呢?首先,會爲該函數開闢一塊臨時的內存空間,這塊內存空被組織成“棧”這種數據結構,作用主要用來存儲函數內部聲明的臨時變量。

每執行一個函數,系統就將函數中的臨時變量組織成棧幀,執行入棧操作,當函數被調用完成的時候,臨時變量已經用不到了,所以要在內存中釋放,執行出棧操作。如以下函數:

function main(){
   let i = 0;
   let j = 1;
   i++;
   j++;
   console.log(i+j)
}
main();

具體的動畫如下:
在這裏插入圖片描述
我們這時要想一個問題,那爲什麼函數會使用棧這種數據結構呢,爲什麼不用隊列、鏈表或者其他數據結構?全體注意,重點來了,以後分析其他的問題也用到一下的方法分析。

因爲函數調用的執行順序符合後進者先出,先進者後出的特點。

比如函數中的局部變量聲明的時間順序,早先定義的變量在內存中保存的時間長,後定義的變量在內存中保存的時間短,所有有一個先後的問題。我們再去腦海中把這種問題的特點抽象成數據結構,只有使用“棧”結構,才符合這種問題。


4.2 棧在表達式中應用

計算機中數字的運算也是使用棧這種數據結構的,我們舉個例子,我們要計算如下表達式:

1 + 2 × 4 - 6

如果比運算符棧頂元素的優先級高,就將當前運算符壓入棧;如果比運算符棧頂元素的優先級低或者相同,從運算符棧中取棧頂運算符,從操作數棧的棧頂取 2 個操作數,然後進行計算,再把計算完的結果壓入操作數棧,繼續比較。動畫如下:
在這裏插入圖片描述

4.3 其他應用

關於棧的應用,還有很多,比如花括號的匹配問題,有關練習去 LeedCode 實踐。這裏就不多舉例子。


❤️ 不要忘記留下你學習的腳印 [點贊 + 收藏 + 評論]

一切看文章不點贊都是“耍流氓”,嘿嘿ヾ(◍°∇°◍)ノ゙!開個玩笑,動一動你的小手,點贊就完事了,你每個人出一份力量(點贊 + 評論)就會讓更多的學習者加入進來!非常感謝! ̄ω ̄=


作者Info:

【作者】:小鹿

【原創公衆號】:小鹿動畫學編程。

【簡介】:和小鹿同學一起用動畫的方式從零基礎學編程,將 Web前端領域、數據結構與算法、網絡原理等通俗易懂的呈獻給小夥伴。先定個小目標,原創 1000 篇的動畫技術文章,和各位小夥伴共同努力一起學習!公衆號回覆 “資料” 送一從零自學資料大禮包!

【轉載說明】:轉載請說明出處,謝謝合作!~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章