初識前端中的棧

什麼是棧?

  • 棧是一種聽從先進後出(Last In First Out )原則的有序集合。新添加的元素保存在棧頂,越早添加的元素越接近棧底。刪除元素只能從棧頂刪除。javascript

    • 舉慄: 吃瓜羣衆圍觀原配打小三(嘿嘿)。越早來的羣衆越接近事發現場(舊元素在棧底),剛來的羣衆只能在外圍(新元素在棧頂)。這時候散場了,外圍的走完裏面的羣衆才能走出來(刪除元素)。
    • 棧也被編程語言的編譯器和內存中保存變量、方法調用等,也被用於瀏覽器歷史記錄(瀏覽器的返回按鈕)
    • 在javascript中有兩種建立棧的方式

    基於數組的棧

class StackArray {
        constructor() {
        this.items = [];
        }
    }
複製代碼

這即是一個基於數組建立的棧,而後咱們要爲它聲明一些方法。java

一、添加元素算法

  • 要注意該方法只添加元素到棧頂,也就是這個數組的末尾。所以咱們能夠用數組的push方法來實現
push(element) {
    this.items.push(element);
  }
複製代碼

二、刪除元素編程

  • 一樣只能從棧頂刪除,由於是基於數組建立的,因此可使用數組的pop方法
pop() {
    return this.items.pop();
  }
複製代碼

三、查看棧頂元素數組

  • 利用數組的長度來查看數組最後一個元素
peek() {
   return this.items[this.items.length - 1];
 }
複製代碼

四、檢查棧是否爲空瀏覽器

  • 若是數組的長度爲零,那它就是空的
isEmpty() {
   return this.items.length === 0;
 }
複製代碼

五、清空棧元素數據結構

  • 能夠直接把數組重置爲空
clear() {
   this.items = [];
 }
複製代碼

這是我舉慄建立的幾個簡單方法,接下來讓我使用這個Stack類編程語言

const stack = new StackArray();
console.log(stack.isEmpty()) // 此時輸出爲true
stack.push(5);      // [] ==>[5]
stack.push(8);     // [5] ==>[5,8]
console.log(stack.peek()); // 輸出爲8
stack.push(11);    // [5,8] ==>[5,8,11]
console.log(stack.isEmpty()) // 這時的輸出爲false
stack.pop();  // [5,8,11] ==>[5,8]
複製代碼
  • 以上即是一個簡單的基於數組的棧。建立一個Stack類最簡單的方式就是用一個數組來存儲元素。在使用數組時,大部分方法的時間複雜度是O(n)。意思是,咱們須要迭代整個數組直到找到要找的那個元素,若是它在棧底,就須要迭代數組的全部位置,其中的n表明數組的長度。若是數組元素越多,所需時間就越長。另外,數組是元素的一個有序集合,爲了保證元素排列有序,它會佔用更多的內存空間。
  • 若是咱們能直接獲取元素,佔用較少的內存空間,而且仍然保證全部元素按照咱們的須要排列,那不是更好嗎?咱們可使用一個javascript對象來存儲全部的棧元素。

基於對象的棧

class Stack{
    constructor(){
        this.count=0;
        this.items = {};
    }
}
複製代碼

這就是一個基於對象的棧,由於對象沒有length屬性,咱們要使用一個count屬性來記錄棧的大小。下面是對象棧的一些簡單的方法。學習

一、添加元素ui

  • 咱們用count變量來做爲對象的鍵名,插入的元素就是它的值,同時在向棧插入元素後,咱們遞增count變量
push(element) {
   this.items[this.count] = element;
   this.count++;
 }
複製代碼

二、檢查棧是否爲空

  • 這個直接判斷count的值就能夠了
isEmpty() {
   return this.count === 0;
複製代碼

三、刪除元素

  • 首先要判斷它是否爲空,(若是爲空那還有啥可刪的嘛!) ==>而後將count屬性減1==>找到棧頂的值並保存==>而後用對象的delete方法將其刪除==>最後作一個像數組的pop方法同樣的功能,將刪除的元素返回
pop() {
   if (this.isEmpty()) { // 判斷是否爲空
     return undefined;
   }
   this.count--; // 棧的大小減1
   const result = this.items[this.count]; // 保存棧頂值
   delete this.items[this.count]; // 刪除棧頂值
   return result; // 返回刪除值
 }
複製代碼

四、建立toString方法

  • 在數組版本中,咱們不須要關心toString方法的實現,由於數據結構能夠直接使用數組原生的toString方法。對於使用對象的版本,咱們來建立一個toString方法來像數組同樣打印出棧的內容。首先,判斷是否爲空==>若是不空就用棧底的第一個元素做爲字符串的初始值==>而後迭代整個棧的鍵==>用上一個相加好的字符串加上當前的字符串,中間用逗號隔開。這樣寫的好處是若是棧只有一個元素,循環將不會執行(畢竟能不循環最好不要嘛)。
toString() {
   if (this.isEmpty()) {
     return '';
   }
   let objString = `${this.items[0]}`;
   for (let i = 1; i < this.count; i++) {
     objString = `${objString},${this.items[i]}`;
   }
   return objString;
 }
複製代碼
  • 好了,實現完toString方法後,咱們就完成了一個簡單的基於對象的棧類。對於使用這個棧的開發者,選擇使用基於數組或者基於對象的版本並不重要。二者均可以實現相同的功能,只是內部實現很不同!!!
除了toString方法,咱們建立其它對象棧方法的複雜度均爲O(1),表明咱們能夠直接找到目標元素並對其進行操做(push、pop)
複製代碼

如何保護數據結構內部元素?

  • 在建立須要別的開發者也可使用的數據結構或對象時,咱們但願保護內部的元素,只有咱們暴露出的方法才能修改內部結構。要確保元素只會被添加到棧頂,而不是其它任何位置。可是咱們上面所聲明的兩種方式並非。那咱們基於對象建立的類來講。舉慄說明:
const stack = new Stack()
console.log(Object.keys(stack));  // ['count','items']
console.log(stack.items); // 能夠直接訪問這個類的內部屬性
複製代碼

咱們能夠經過Object.keys方法來直接獲取這個對象上的全部屬性,而後咱們就能對暴露出來的屬性賦新的值。這是不容許的!接下來咱們來看如何使用Javascript來實現私有屬性的方法。

一、下劃線命名約定

  • 有的開發者喜歡在Javascript中使用下劃線命名約定來標記一個屬性爲私有屬性。
class Stack {
    constructor() {
      this._count = ();
      this._items = {};
    }
}
複製代碼
  • 但這種方式只是一種約定,並不能保護數據,並且只能依賴於使用咱們代碼的開發者所具有的常識

二、用ES6的WeakMap實現類

  • ES6的WeakMap能夠存儲鍵值對,其中鍵是對象,值能夠是任意數據類型。咱們來寫一個用WeakMap來存儲items屬性的數組版本的類
const items = new WeakMap();  // 聲明一個WeakMap類型的變量items
class Stack {
    constructor () {
        items.set(this,[]); // 在constructor中,以this爲鍵,把表明棧的數組存入items
    }
    push(element){
        const s = items.get(this); // 從WeakMap中取出值,即以this爲鍵從items中取值
        s.push(element);
    }
    pop(){
        const s = items.get(this);
        const r = s.pop();
        return r;
    }
}
複製代碼
  • 這樣作,items在Stack類裏是真正的私有屬性。採用這種方法,代碼的可讀性不強,並且在擴展該類時沒法繼承私有屬性。兩種方式也是都有各自的優缺點,魚與熊掌不可兼得呦。

最後

  • 本文的內容來自本人閱讀過《學習Javascript數據結構與算法》後按照我的的理解對第四章的部份內容作得一個簡單的整理,若是各位看官以爲還能夠,請給小弟點個贊。我是一個努力幹活還不磨人的小妖精~~~
相關文章
相關標籤/搜索