《學習JavaScript數據結構與算法》(下文中簡稱《學》)讀書筆記。
文章首發於個人博客javascript
棧是一種聽從後進先出(Last In First Out, LIFO)的有序集合。新添加的元素保存在棧的末尾,稱做棧頂,另外一端叫棧底。java
通俗點講就好像咱進電梯,後進的人先出來(電梯大了順序亂了啥的別計較)。git
建立一個類來表示棧,選擇數組來保存棧裏的元素:github
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;
咱們能夠對上面代碼作個測試:數據結構
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
下圖描述了咱們上面測試代碼的操做(圖片來源:《學》):學習
十進制轉爲其餘進制測試
轉爲二進制圖解,其餘進制也相似:ui
代碼實現(使用了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)的有序的數據結構。在尾部添加新元素,並從頂部移除元素。
建立一個類來表示隊列,用數組存儲隊列中元素的數據結構:
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;
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"
優先隊列的添加和移除是基於優先級的。顯示中的例子是頭等艙,再有就是急診室等。
實現一個優先隊列,有兩種選項:設置優先級,而後在正確的位置添加元素;或者用入列操做元素,而後按照優先級移除它們。咱們使用第一種方式進行演示:
function PriorityQueue() { this.items = []; } /** * 要添加到隊列的元素及其在隊列中的優先級 * @param {[type]} element * @param {[number]} priority 優先級 */ 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) { // 最小優先隊列 // 還能保證優先級相同時也遵循隊列先進先出的原則 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();
下圖能夠看到上面測試代碼的運行結果(圖片來源:《學》):
第一個被添加的元素是優先級爲2的John。由於此前隊列爲空,因此它是隊列中惟一的元素。接下來,添加了優先級爲1的Jack。因爲Jack的優先級高於John,它就成了隊列中的第一個元素。 而後, 添加了優先級也爲1的Camila。 Camila的優先級和Jack相同,因此它會被插入到Jack以後(由於Jack先被插入隊列); Camila的優先級高於John,因此它會被插入到John以前。
擊鼓傳花遊戲:人們圍成一個圈,把花盡快傳給旁邊的人。某一時刻傳花中止,此時花在誰手裏誰就退出圓圈結束遊戲。重複此過程,直到只剩一個勝者。
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
下圖模擬了這個輸出過程(圖片來源:《學》):