說說你對棧、佇列的理解?應用場景?

林恒發表於2024-04-11

一、棧

棧(stack)又名堆疊,它是一種運算受限的線性表,限定僅在表尾進行插入和刪除操作的線性表

表尾這一端被稱為棧頂,相反地另一端被稱為棧底,向棧頂插入元素被稱為進棧、入棧、壓棧,從棧頂刪除元素又稱作出棧

所以其按照先進後出的原則儲存資料,先進入的資料被壓入棧底,最後的資料在棧頂,需要讀資料的時候從棧頂開始彈出資料,具有記憶作用

關於棧的簡單實現,如下:

class Stack {
  constructor() {
    this.items = [];
  }

  /**
   * 新增一個(或幾個)新元素到棧頂
   * @param {*} element 新元素
   */
  push(element) {
    this.items.push(element)
  }

  /**
   * 移除棧頂的元素,同時返回被移除的元素
   */
  pop() {
    return this.items.pop()
  }

  /**
   * 返回棧頂的元素,不對棧做任何修改(這個方法不會移除棧頂的元素,僅僅返回它)
   */
  peek() {
    return this.items[this.items.length - 1]
  }

  /**
   * 如果棧裡沒有任何元素就返回true,否則返回false
   */
  isEmpty() {
    return this.items.length === 0
  }

  /**
   * 移除棧裡的所有元素
   */
  clear() {
    this.items = []
  }

  /**
   * 返回棧裡的元素個數。這個方法和陣列的length屬性很類似
   */
  size() {
    return this.items.length
  }
}

關於棧的操作主要的方法如下:

  • push:入棧操作
  • pop:出棧操作

二、佇列

跟棧十分相似,佇列是一種特殊的線性表,特殊之處在於它只允許在表的前端(front)進行刪除操作,而在表的後端(rear)進行插入操作

進行插入操作的端稱為隊尾,進行刪除操作的端稱為隊頭,當佇列中沒有元素時,稱為空佇列

在佇列中插入一個佇列元素稱為入隊,從佇列中刪除一個佇列元素稱為出隊。因為佇列只允許在一端插入,在另一端刪除,所以只有最早進入佇列的元素才能最先從佇列中刪除,故佇列又稱為先進先出

簡單實現一個佇列的方式,如下:

class Queue {
    constructor() {
        this.list = []
        this.frontIndex = 0
        this.tailIndex = 0
    }
    enqueue(item) {
        this.list[this.tailIndex++] = item
    }
    unqueue() {
        const item  = this.list[this.frontIndex]
        this.frontIndex++        
        return item
    }
}

上述這種入隊和出隊操作中,頭尾指標只增加不減小,致使被刪元素的空間永遠無法重新利用

當佇列中實際的元素個數遠遠小於向量空間的規模時,也可能由於尾指標已超越向量空間的上界而不能做入隊操作,出該現象稱為"假溢"

在實際使用佇列時,為了使佇列空間能重複使用,往往對佇列的使用方法稍加改進:

無論插入或刪除,一旦rear指標增1或front指標增1 時超出了所分配的佇列空間,就讓它指向這片連續空間的起始位置,這種佇列也就是迴圈佇列

下面實現一個迴圈佇列,如下:

class Queue {
    constructor(size) {
        this.size = size; // 長度需要限制, 來達到空間的利用, 代表空間的長度
        this.list = [];
        this.font = 0; // 指向首元素
        this.rear = 0;  // 指向準備插入元素的位置
    }
    enQueue() {
        if (this.isFull() == true) {
            return false
        }
        this.rear = this.rear % this.k;
        this._data[this.rear++] = value;
        return true
    }
    deQueue() {
        if(this.isEmpty()){
            return false;
        }
        this.font++;
        this.font = this.font % this.k;
        return true;
    }
    isEmpty() {
        return this.font == this.rear - 1;
    }
    isFull() {
        return this.rear % this.k == this.font;
    }
}

上述透過求餘的形式代表首尾指標增1 時超出了所分配的佇列空間

三、應用場景

藉助棧的先進後出的特性,可以簡單實現一個逆序數處的功能,首先把所有元素依次入棧,然後把所有元素出棧並輸出

包括編譯器的在對輸入的語法進行分析的時候,例如"()""{}""[]"這些成對出現的符號,藉助棧的特性,凡是遇到括號的前半部分,即把這個元素入棧,凡是遇到括號的後半部分就比對棧頂元素是否該元素相匹配,如果匹配,則前半部分出棧,否則就是匹配出錯

包括函式呼叫和遞迴的時候,每呼叫一個函式,底層都會進行入棧操作,出棧則返回函式的返回值

生活中的例子,可以把乒乓球盒比喻成一個堆疊,球一個一個放進去(入棧),最先放進去的要等其後面的全部拿出來後才能出來(出棧),這種就是典型的先進後出模型

佇列

當我們需要按照一定的順序來處理資料,而該資料的資料量在不斷地變化的時候,則需要佇列來幫助解題

佇列的使用廣泛應用在廣度優先搜尋種,例如層次遍歷一個二叉樹的節點值(後續將到)

生活中的例子,排隊買票,排在隊頭的永遠先處理,後面的必須等到前面的全部處理完畢再進行處理,這也是典型的先進先出模型

參考文獻

  • https://baike.baidu.com/item/%E6%A0%88/12808149
  • https://baike.baidu.com/item/%E9%98%9F%E5%88%97/14580481

如果對您有所幫助,歡迎您點個關注,我會定時更新技術文件,大家一起討論學習,一起進步。

說說你對棧、佇列的理解?應用場景?

相關文章