數據結構-棧

前言

數組是 JS 中最經常使用的數據結構,它能夠在任意位置添加或刪除數據。棧是另一種數據結構,相似於數組,可是在添加或刪除數據時更加靈活。git

棧數據結構

棧是一種 後進先出(LIFO) 的數據結構。新添加或待刪除的元素都保存在棧的一端,叫 棧頂 ,另外一端就叫作 棧底 。在棧中,新元素都靠近棧頂,就元素都靠近棧底。算法

建立棧

能夠用數組來模擬一個棧結構:數組

function Stack() {
    let items = []
    // 棧的屬性和方法
}

須要實現的方法:數據結構

  • push(element): 添加一個元素到棧頂
  • pop(): 移除棧頂的元素,並返回該元素
  • peek(): 僅僅返回棧頂的元素
  • clear(): 清空棧
  • size(): 返回棧中的元素的個數
  • isEmpty(): 判斷棧是否爲空
// 向棧中添加元素
this.push = function (element) {
    items.push(element)
}
// 從棧中移除元素
this.pop = function () {
    return items.pop()
}
// 查看棧頂元素
this.peek = function () {
    return items[item.length - 1]
}
// 檢查棧是否爲空
this.isEmpty = function () {
    return !!item.length
}
// 清空棧中的元素
this.clear = function () {
    items = []
}
// 返回棧的大小
this.size = function () {
    return items.length
}
// 打印棧
this.print = function () {
    console.log(items.toString())
}

ES6 與 棧

ES6 的寫法:閉包

class Stack {
    constructor() {
        this.items = []
    }
    push (element) {
        this.items.push(element)
    }
    // ... 其餘方法
}

ES6 的類是基於原型的,雖然基於原型的類比基於函數的類更節省內存,可是卻不能聲明私有變量,因此變量 items 是公共的。這種狀況下,能夠直接經過修改 items 來修改棧中的數據,這是沒法避免的。ide

用 ES6 的限定做用域 Symbol 實現類

ES6 新增了 Symbol 基礎類型,它是不可變的,也能夠做用對象的屬性。函數

let _items = Symbol()
class Stack {
    constructor() {
        this[_items] = []
    }
    // ... 其餘方法
}

上面這個例子建立了一個假的私有屬性,不能徹底規避上面提到的問題,由於 ES6 新增的 Object.getOwnPropertySymbols 方法可以取到類裏面聲明的全部 Symbols 屬性,好比:this

let stack = new Stack()
stack.push(66)
stack.push(88)
let objectSymbols = Object.getOwnPropertySymbols(stack)
console.log(objectSymbols.length) // 1
console.log(objectSymbols[0]) // Symbol()
stack[objectSymbols[0]].push(1)
stack.print() // 66 88 1
經過訪問 stack[objectSymbols[0]] 是能夠訪問 _items 的,而且能夠對 _items 進行任意操做。

用 ES6 的WeakMap 實現類

有一種數據類型能夠確保屬性是私有的,這就是 WeakMap 。WeakMap 能夠存儲鍵值對,其中鍵是對象,值能夠是任意數據類型。code

const items = new WeakMap()
class Stack {
    constructor() {
        items.set(this, [])
    }
    push(element) {
        let s = items.get(this)
        s.push(element)
    }
    pop() {
        let s = items.get(this)
        return s.pop()
    }
    // ... 其餘方法
}

如今,Stack 中的 items 是私有的了,可是 items 是在 Stack 類之外聲明的,仍是能夠被改動,因此須要藉助閉包來實現一層封裝:對象

let Stack = (function () {
    const items = new WeakMap()
    class Stack {
    constructor() {
        items.set(this, [])
    }
    // ... 其餘方法
    return Stack
}
})()

### 用棧解決實際問題
棧在 JS 中應用仍是十分普遍的,好比 調用棧 。進制轉換也是很常見的例子,能夠用 棧 來處理,好比要把十進制轉化成二進制,能夠將該十進制數字和2整除,直到結果是 0 爲止。

function divideBy2 (decNumber) {
    var remStack = new Stack(),
        rem,
        binaryString = ''
   
   while (decNumber > 0) {
       rem = Math.floor(decNumber % 2)
       remStack.push(rem)
       decNumber = Math.floor(decNumber / 2)
   }
   while (!remStack.isEmpty()) {
       binaryString += remStack.pop().toString()
   }
   return binaryString
}

這個例子中,當結果知足和2作整除的條件是,會取得當前結果和2的餘數,放到棧裏,而後讓結果繼續和2作整除。

#### 改進算法
把上面的例子改爲十進制轉成任意進制的:

function baseConverter(decNumber, base) {
     var remStack = new Stack(),
        rem,
        binaryString = '',
        digits = '0123456789ABCDEF'
   
     while (decNumber > 0) {
        rem = Math.floor(decNumber % base)
        remStack.push(rem)
        decNumber = Math.floor(decNumber / base)
     }
     while (!remStack.isEmpty()) {
        binaryString += digits[remStack.pop()]
     }
   return binaryString
}
相關文章
相關標籤/搜索