JavaScript-數據結構與算法(一):棧與隊列

《學習JavaScript數據結構與算法》(下文中簡稱《學》)讀書筆記邑时。
文章首發(fā)于我的博客

棧是一種遵從后進先出(Last In First Out, LIFO)的有序集合。新添加的元素保存在棧的末尾良拼,稱作棧頂设易,另一端叫棧底舟扎。

通俗點講就好像咱進電梯姻锁,后進的人先出來(電梯大了順序亂了啥的別計較)肮雨。

創(chuàng)建棧

創(chuàng)建一個類來表示棧,選擇數組來保存棧里的元素:

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

實現一個棧梭稚,通常要實現以下幾種方法:

方法 功能
push() 添加一個(或幾個)新元素到棧頂
pop() 移除棧頂元素颖低,同時返回該元素
peek() 返回棧頂元素
isEmpty() 判空
clear() 清空
size() 棧里元素個數

實現后的代碼:

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

Stack.prototype = {
  push: function (element) {
    this.items.push(element)
  },
  pop: function () {
    return this.items.pop();
  },
  peek: function () {
    return this.items[this.items.length - 1];
  },
  isEmpty: function () {
    return this.items.length === 0;
  },
  size: function () {
    return this.items.length;
  },
  clear: function () {
    this.items = [];
  },
  print: function () {
    console.log(this.items.toString());
  }
};

module.exports = Stack;

使用Stack類

我們可以對上面代碼做個測試:

var Stack = require('./Stack');

var stack = new Stack();

console.log(stack.isEmpty());  // true

stack.push(5);
stack.push(8);
console.log(stack.peek());

stack.push(11);
console.log(stack.size());  // 3
console.log(stack.isEmpty());  // false

stack.push(15);

stack.pop();
stack.pop();
console.log(stack.size());  // 2
stack.print();  // 5, 8

下圖描述了我們上面測試代碼的操作(圖片來源:《學》):

push操作
pop操作

應用示例

十進制轉為其他進制

轉為二進制圖解,其他進制也類似:

十進制=>二進制

代碼實現(使用了ES6語法默認參數弧烤,也可以去掉= 2):

var Stack = require('./Stack');

function baseConverter(decNumber, base = 2) { // ES6語法忱屑,默認參數
  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;
}

// test
console.log(baseConverter(233));
console.log(baseConverter(10, 8));
console.log(baseConverter(1000, 16));

判斷給定字符串是否是回文

let Stack = require('./Stack');

let isPalindrome = (str) => {
  let stack = new Stack();
  
  for (let i = 0; i < str.length; i++) {
    stack.push(str[i]);
  }
  
  let rstr = '';
  while(str.size() > 0) {
    rstr += stack.pop();
  }
  return str === rstr ? true : false;
};

模擬遞歸

// 階乘
function factorial(n) {
  n === 0 && return 1;
  return n * factorial(n - 1);
}
let Stack = require('./Stack');

let fact = (n) => {
  let stack = new Stack();
  
  while (n > 1) {
    stack.push(n--);
  }
  
  let product = 1;
  while (stack.size() > 0) {
    product *= stack.pop();
  }
  return product;
};

隊列

隊列是一種先進先出(First In First Out, FIFO)的有序的數據結構。在尾部添加新元素,并從頂部移除元素莺戒。

創(chuàng)建隊列

創(chuàng)建一個類來表示隊列粱栖,用數組存儲隊列中元素的數據結構:

function Queue() {
  this.items = [];
}

實現一個隊列,通常要實現以下幾種方法:

方法 功能
enqueue() 向隊列尾部添加一個(或多個)新的項
dequeue() 移除隊列隊首元素并返回
front() 返回隊列中第一個元素
isEmpty() 判空
size()

實現后的代碼:

function Queue() {
  this.items = [];
}

Queue.prototype = {
  enqueue: function (element) {
    this.items.push(element)
  },
  dequeue: function () {
    return this.items.shift();
  },
  front: function () {
    return this.items[0];
  },
  isEmpty: function () {
    return this.items.length === 0;
  },
  size: function () {
    return this.items.length;
  },
  clear: function () {
    this.items = [];
  },
  print: function () {
    console.log(this.items.toString());
  }
};

module.exports = Queue;

使用Queue類

var Queue = require('./Queue');

var queue = new Queue();
console.log(queue.isEmpty());

queue.enqueue('John');
queue.enqueue('Bob');

queue.enqueue('Lily');

queue.print(); // "John", "Bob", "Lily"
console.log(queue.size()); // 3
queue.dequeue();
queue.print(); // "Bob", "Lily"

優(yōu)先隊列

優(yōu)先隊列的添加和移除是基于優(yōu)先級的脏毯。顯示中的例子是頭等艙,再有就是急診室等幔崖。

實現一個優(yōu)先隊列食店,有兩種選項:設置優(yōu)先級,然后在正確的位置添加元素赏寇;或者用入列操作元素吉嫩,然后按照優(yōu)先級移除它們。我們使用第一種方式進行演示:

function PriorityQueue() {
  this.items = [];
}

/**
 * 要添加到隊列的元素及其在隊列中的優(yōu)先級
 * @param {[type]} element
 * @param {[number]} priority 優(yōu)先級
 */
function QueueElement(element, priority) {
  this.element = element;
  this.priority = priority;
}

PriorityQueue.prototype = {
  enqueue: function (element, priority) {
    var queueElement = new QueueElement(element, priority);

    if (this.isEmpty()) {
      // 如果隊列為空嗅定,元素直接入列
      this.items.push(queueElement);
    } else {
      var added = false;
      for (var i = 0; i < this.items.length; i++) {
        if (queueElement.priority < this.items[i].priority) { // 最小優(yōu)先隊列
          // 還能保證優(yōu)先級相同時也遵循隊列先進先出的原則
          this.items.splice(i, 0, queueElement);
          added = true;
          break;
        }
      }
      // 要添加元素的priority值大于任何已有的元素自娩,把它添加到隊列的末尾
      if (!added) items.push(queueElement);
    }
  }
  // 其他方法和默認的Queue實現相同
};

測試:

var priorityQueue = new PriorityQueue();
priorityQueue.enqueue('John', 2);
priorityQueue.enqueue('Jack', 1);
priorityQueue.enqueue('Camila', 1);
priorityQueue.print();

下圖可以看到上面測試代碼的運行結果(圖片來源:《學》):

queue

第一個被添加的元素是優(yōu)先級為2的John。因為此前隊列為空渠退,所以它是隊列中唯一的元素忙迁。接下來,添加了優(yōu)先級為1的Jack碎乃。由于Jack的優(yōu)先級高于John姊扔,它就成了隊列中的第一個元素。 然后梅誓, 添加了優(yōu)先級也為1的Camila恰梢。 Camila的優(yōu)先級和Jack相同,所以它會被插入到Jack之后(因為Jack先被插入隊列)梗掰; Camila的優(yōu)先級高于John嵌言,所以它會被插入到John之前。

循環(huán)隊列——擊鼓傳花

擊鼓傳花游戲:人們圍成一個圈及穗,把花盡快傳給旁邊的人摧茴。某一時刻傳花停止,此時花在誰手里誰就退出圓圈結束游戲拥坛。重復此過程蓬蝶,直到只剩一個勝者。

var Queue = require('./Queue');
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();
}

測試代碼:

var names = ['John', 'Jack', 'Camila', 'Ingrid', 'Carl'];
var winner = hotPotato(names, 7);
console.log('the winner is : ' + winner);

輸出:

Camila在擊鼓傳花游戲中被淘汰
Jack在擊鼓傳花游戲中被淘汰
Carl在擊鼓傳花游戲中被淘汰
Ingrid在擊鼓傳花游戲中被淘汰
the winner is : John

下圖模擬了這個輸出過程(圖片來源:《學》):

循環(huán)隊列
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末缓窜,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌禾锤,老刑警劉巖私股,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異恩掷,居然都是意外死亡倡鲸,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門黄娘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來峭状,“玉大人,你說我怎么就攤上這事逼争∮糯玻” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵誓焦,是天一觀的道長胆敞。 經常有香客問我,道長杂伟,這世上最難降的妖魔是什么移层? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮赫粥,結果婚禮上幽钢,老公的妹妹穿的比我還像新娘。我一直安慰自己傅是,他們只是感情好匪燕,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著喧笔,像睡著了一般帽驯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上书闸,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天尼变,我揣著相機與錄音,去河邊找鬼浆劲。 笑死嫌术,一個胖子當著我的面吹牛,可吹牛的內容都是我干的牌借。 我是一名探鬼主播度气,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼膨报!你這毒婦竟也來了磷籍?” 一聲冷哼從身側響起适荣,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎院领,沒想到半個月后弛矛,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡比然,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年丈氓,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片强法。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡扒寄,死狀恐怖,靈堂內的尸體忽然破棺而出拟烫,到底是詐尸還是另有隱情,我是刑警寧澤迄本,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布硕淑,位于F島的核電站,受9級特大地震影響嘉赎,放射性物質發(fā)生泄漏置媳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一公条、第九天 我趴在偏房一處隱蔽的房頂上張望拇囊。 院中可真熱鬧,春花似錦靶橱、人聲如沸寥袭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽传黄。三九已至,卻和暖如春队寇,著一層夾襖步出監(jiān)牢的瞬間膘掰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工佳遣, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留识埋,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓零渐,卻偏偏與公主長得像窒舟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子诵盼,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內容