學習數據結構與算法之棧與隊列

本系列全部文章:
第一篇文章:學習數據結構與算法之棧與隊列
第二篇文章:學習數據結構與算法之鏈表
第三篇文章:學習數據結構與算法之集合
第四篇文章:學習數據結構與算法之字典和散列表
第五篇文章:學習數據結構與算法之二叉搜索樹javascript

原由

最近要準備校招,打開某網站準備開始刷題,發現算法題根本沒法動手,因而以爲這塊須要惡補。(⊙v⊙)嗯,至少得先知道概念吧。因而翻出了機房裏的這本《學習JavaScript數據結構與算法》開始學習程序員的基礎知識。這本書用了我最熟悉的JS來實現各類數據結構和算法,並且書很薄,能夠說是一本不錯的入門教程。雖然我是個前端,可是計算機基礎不能丟下。前端

棧能夠理解爲一種特殊的數組。遵循後進先出(LIFO)的原則,元素在棧頂添加和刪除。生活中經常使用來比做棧的例子主要是一疊盤子或一堆書,可是我以爲不夠形象,由於盤子或書能夠從中間被抽走。因此我通常把棧當作彈匣,想象一會兒彈被一個個推動彈匣中,好比下圖:java

棧

(圖片來自谷歌搜索,侵刪)git

用JavaScript實現棧

聲明一個構造函數:程序員

function Stack () {
  // 使用數組來保存棧元素
  var items = []
}

爲棧聲明一些方法:github

  • push(elements(s)):添加一個或多個元素到棧頂
  • pop():刪除位於棧頂的元素,並返回該元素
  • peek():返回棧頂元素
  • isEmpty():當前棧爲空則返回true,不然爲false
  • size():返回棧的元素個數
  • clear():清空棧

實現push

用數組的push方法向數組末尾添加新元素,實現元素入棧算法

// 棧頂添加
this.push = function (element) {
  items.push(element)
}

實現pop

用數組的pop方法在數組末尾刪除一個元素,並返回刪除元素,實現元素出棧segmentfault

// 棧頂刪除並返回刪除元素
this.pop = function () {
  return items.pop()
}

實現peek

棧頂就是數組最後一個元素,使用Array[Array.length - 1]得到數組

// 返回棧頂元素
this.peek = function () {
  return items[items.length - 1]
}

實現其餘方法

隊列裏面也用了這些方法,爲避免重複,就先單獨拿出來了。數據結構

// 棧是否爲空
this.isEmpty = function () {
  return items.length === 0
}

// 返回棧裏的元素個數
this.size = function () {
  return items.length
}

// 清空棧
this.clear = function () {
  items = []
}

// 打印棧
this.print = function () {
  console.log(items.toString())
}

棧的應用

書上的例子有將十進制轉二進制,這裏我把後面那個十進制轉任意進制的代碼貼出來。

十進制轉二進制原理很簡單:把十進制數不斷除以2直到爲0,而後把每次的餘數拼接到一塊兒就是二進制數。

轉其餘進制也是相似的方法,只不過是把除以2換成其餘數而已。代碼以下:

// 把十進制轉成任何進制
function BaseConverter (decNumber, base) {
  var remStack = new Stack(),
      rem,
      binaryString = '',
      digits = '0123456789ABCDEF'

  // 判斷十進制數是否爲0,把餘數推入棧中
  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
}

這裏的decNumber是要轉換的十進制數,base是要轉換的進制,remStack是上面Stack的實例,在remStack中操做棧的方法。這裏的digits是對打印出來的數作一個處理,好比十六進制的數餘數會大於9,那麼就要用A、B、C、D、E、sF來表示10~15。

棧的學習暫時就這樣了,這裏貼上代碼地址,有興趣的能夠看看:

棧的實現-源代碼

隊列

和棧很相似,只是原則不一樣,隊列是先進先出(FIFO),又稱先來先服務。隊列在頭部刪除元素,尾部添加元素。生活中隊列的例子就是排隊了,這也很容易理解。

用JavaScript實現隊列

一樣聲明構造函數:

function Queue () {
  var items = []
}

隊列的方法:

  • enqueue(elements(s)):向隊列尾部添加一個或多個元素
  • dequeue():移除隊列頭部的元素並返回
  • front():返回隊列頭部的元素

其餘的和棧同樣。

實現enqueue

用push方法推入元素

// 向隊列尾部添加元素
this.enqueue = function (element) {
  items.push(element)
}

實現dequeue

用shift方法刪除第一個數組元素,並返回刪除的元素

// 刪除隊列頭部的元素並返回刪除元素
this.dequeue = function () {
  return items.shift()
}

實現front

直接返回第一個數組元素

// 返回隊列頭部的元素
this.front = function () {
  return items[0]
}

隊列的應用

書上有講優先隊列和循環隊列的應用,這裏就簡單講一下優先隊列的原理:

要實現優先隊列有兩種思路:一是將元素按正確的位置添加到隊列中,而後元素正常在隊列頭部被刪除;二是元素在隊列尾部不按優先級正常入列,而後按優先級刪除對應元素。

書上是按第一種思路實現的優先隊列,這裏限於篇幅就貼上代碼地址:

隊列的實現-源代碼

接下來談談循環隊列的應用——擊鼓傳花遊戲

function hotPotato (nameList, num) {
  var queue = new Queue()

  // 參與者的名字入列
  for (var i = 0; i < nameList.length; i++) {
    queue.enqueue(nameList[i])
  }

  var eliminated = ''

  // 隊列中的最後一我的爲勝者
  while (queue.size() > 1) {
    // 按設定的擊鼓次數,每一個人都從隊列頭部出列轉到隊列尾部(模擬傳花)
    for (var i = 0; i < num; i++) {
      queue.enqueue(queue.dequeue())
    }
    // 到了規定次數後,在隊列頭部的人(至關於拿到花)被淘汰
    eliminated = queue.dequeue()
    console.log(eliminated + '在擊鼓傳花遊戲中被淘汰')
  }

  // 勝者出列並被返回
  return queue.dequeue()
}

其中nameList是參與遊戲的名字列表,num是擊鼓次數。

隊列學習就暫時到這裏,明天繼續學習鏈表。

相關文章
相關標籤/搜索