Javascript數據結構與算法--棧的實現與用法

棧數據結構

棧是一種聽從後進先出(LIFO)原則的有序集合。新添加的或者待刪除的元素都保存在棧的同一端,稱做棧頂,另外一端就叫棧底。在棧裏,新元素都靠近棧頂,舊元素都接近棧底。javascript

咱們在生活中常能看到棧的例子。整齊堆起來的書,廚房堆起的盤子等java

棧也被用在編程語言的編譯器和內存中保存變量、方法調用等。git

咱們能夠選擇不一樣的數據結構來保存棧中的元素,在這裏,咱們選擇數組來保存棧中的元素。es6

/**
 * 使用es6中的Class語法來編寫類:棧
 * 此棧使用數組來保存元素
 */
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
  }

  /**
   * 返回以字符串形式輸出的棧
   */
  toString() {
    return this.items.toString()
  }

  /**
   * 返回以數組形式輸出的棧
   */
  toArray() {
    return this.items
  }

}

棧的用法

進制轉換

更多詳情請查看源碼
在計算機裏全部的內容都是二進制數字表示的(0和1),而咱們生活中主要用到十進制,還有16進制等。那麼就須要進制轉換。咱們能夠利用棧來實現轉換。github

/**
 * 10進制數轉換爲其餘16進制之內進制的數
 * @param {*} decNumber 須要轉換的數
 * @param {Int32Array} hex 進制數
 */
function hexConverter(decNumber, hex) {
  let remStack = new Stack()
  let rem = 0
  let baseString = ''
  let digits = '0123456789ABCDEF' //進製取數

  if (hex < 2 || hex > 16) {
    return '只轉換大於二進制小於十六進制之間的進制'
  }
  while (decNumber > 0) {
    rem = Math.floor(decNumber % hex) // 求模運算
    remStack.push(rem)
    decNumber = Math.floor(decNumber / hex) // 除運算
  }

  while (!remStack.isEmpty()) {
    baseString += digits[remStack.pop()] // 取出棧中的數據對應於進制數的表示數
  }

  return baseString
}

迴文判斷

更多詳情請查看源碼
正讀反讀都相同的字符序列稱爲迴文,例如「abccba」、「abcba」、「12321」、「123321」。
迴文判斷有不少種方法,在這裏,咱們能夠採用先入棧後出棧的方法,來比較入棧以前,和出棧以後兩個字符串是否相同。
空字符串究竟是不是迴文呢,有點疑惑? 我這裏定義爲不是迴文。編程

/**
 * 判斷字符串是否爲迴文
 * @param {String} str 要判斷的字符串
 */
 function palindrome(str) {
  // 非string類型的 或者  空字符串  直接判斷不是迴文
  if (typeof (str) !== 'string' || str.length === 0) {
    return false
  }

  let stack = new Stack()
  let oStr = str.toLocaleLowerCase()
  let nStr = ''

  for (let i = 0; i < str.length; i++) {
    stack.push(str[i])
  }

  while (!stack.isEmpty()) {
    nStr += stack.pop().toLocaleLowerCase()
  }

  if (nStr === oStr) {
    // return `輸入的字符串【{$oStr}】是迴文`
    return true
  } else {
    // return `輸入的字符串【{$oStr}】不是迴文`
    return false
  }
}

平衡括號

更多詳情請查看源碼
若是一個括號序列包含完整的左右括號對,則稱爲平衡括號序列。如:"{[()]}","", "({})", "{()}"都是平衡括號,而"{()[]", ")"則不是平衡括號。數組

括號只有三種()/[]/{},每種分別有左右括號。咱們能夠這樣操做:遇到左括號,左括號入棧,遇到右括號,取出棧中最後一個括號來比對的方式來判斷是否相等平衡。數據結構

/**
 * 判斷括號序列是否平衡,空序列也算是平衡
 * @param {String} brackets 括號序列
 */
function balanceBracket(brackets) {
  if (typeof (brackets) !== 'string') {
    return false
  }

  let left = '([{'
  let right = ')]}'
  let num = 0 // 括號的對數
  let stack = new Stack()

  for (let i = 0; i < brackets.length; i++) {
    if (right.indexOf(brackets[0]) > -1) {
      return false
    }
    if (left.indexOf(brackets[i]) > -1) {
      stack.push(brackets[i])
      num++
    } else {
      if (right.indexOf(brackets[i]) > -1) {
        let topBracket = stack.pop()
        let rightSort = right.indexOf(brackets[i])
        let leftSort = left.indexOf(topBracket)

        if (rightSort !== leftSort) {
          return false
        }
      }
    }
  }
  if (!stack.isEmpty()) return false // 2019-5-30 更新,考慮狀況 [[[()
  return `是平衡括號序列。有${num}對括號`
}

漢諾塔

更多詳情請查看源碼
有三根相鄰的柱子,標號爲A,B,C。A柱子上從下到上按金字塔狀疊放着n個不一樣大小的圓盤,要把全部圓盤移動到B柱子上。
要求:編程語言

  1. 每次只能移動一個圓盤。
  2. 每根柱子上的圓盤,下面的都比上面的大。

問題:
請問至少須要移動多少次圓盤?每次移動的步驟是怎樣的?this

let num = 0 // 記錄移動的次數

/**
 * 記錄圓盤移動的過程
 *
 * 這裏的思路,一直在循環作一件事情。
 * 把原始柱子上的圓盤分爲兩部分,最大和其它。
 * 第一回合,將其它移動到輔助柱子上,將最大的移動到目標柱子上,再將其它移動到目標柱子上
 * 第二回合,將其它移動到輔助柱子上,將最大的移動到目標柱子上,再將其它移動到目標柱子上
 * ...
 * 第2 ** n - 1回合,將其它移動到輔助柱子上,將最大的移動到目標柱子上,再將其它移動到目標柱子上
 *
 * 可是,這裏源柱子、輔助柱子和目標柱子會隨着其它盤而變更。
 * 其它盤在哪一個柱子上,哪根柱子就是源柱子。

 * @param {Int32Array} plates 圓盤個數
 * @param {Array} source 源柱子
 * @param {Array} helper 輔助柱子
 * @param {Array} dest 目的地柱子
 * @param {String} sourceName 源柱子的名字
 * @param {String} helperName 輔助柱子的名字
 * @param {String} destName 目的地柱子的名字
 * @param {Array} moves 步驟存儲器,存儲每一步的流程
 */
function moveOfHanoi(
  plates,
  source,
  helper,
  dest,
  sourceName,
  helperName,
  destName,
  moves = []
) {
  if (plates <= 0) {
    return moves
  } else if (plates === 1) {
    // 彈出源柱子上剩下的最大圓盤,並將其壓入目標柱子
    dest.push(source.pop())
    num++
    let sourceArr = source.toString()
    let helperArr = helper.toString()
    let destArr = dest.toString()
    let movestr = `第 ${num} 步,將圓盤 ${plates} 從 ${sourceName} 移至 ${destName}; ${sourceName}: [${sourceArr}],${helperName}: [${helperArr}],${destName}: [${destArr}]`
    moves.push(movestr)
  } else {
    moveOfHanoi(
      plates - 1,
      source,
      dest,
      helper,
      sourceName,
      destName,
      helperName,
      moves
    )

    // 彈出源柱子上剩下的最大圓盤,並將其壓入目標柱子
    dest.push(source.pop())
    num++
    let sourceArr = source.toString()
    let helperArr = helper.toString()
    let destArr = dest.toString()
    let movestr = `第 ${num} 步,將圓盤 ${plates} 從 ${sourceName} 移至 ${destName}; ${sourceName}: [${sourceArr}],${helperName}: [${helperArr}],${destName}: [${destArr}]`
    moves.push(movestr)

    moveOfHanoi(
      plates - 1,
      helper,
      source,
      dest,
      helperName,
      sourceName,
      destName,
      moves
    )
  }
  return moves
}

/**
 * 漢諾塔
 * 記錄每一次圓盤移動的動做。從${源柱子}到${目標柱子}
 * @param {Int32Array} plates 圓盤的個數
 * @param {String} sourceName 源柱子的名稱
 * @param {String} helperName 輔助柱子的名稱
 * @param {String} destName 目標柱子的名稱
 */
function hanoiStackArray(plates, sourceName, helperName, destName) {
  let source = new Stack()
  let helper = new Stack()
  let dest = new Stack()

  for (let i = plates; i > 0; i--) {
    source.push(i)
  }
  num = 0
  return moveOfHanoi(
    plates,
    source,
    helper,
    dest,
    sourceName,
    helperName,
    destName
  )
}

[完]

相關文章
相關標籤/搜索