diff --git a/CHANGELOG.md b/CHANGELOG.md index e48741f9..36af93c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +# [2.0.0](https://github.com/amejiarosario/dsa.js/compare/1.18.0...2.0.0) (2020-09-08) + + +### Features + +* **book/pq:** add exercise with pq and dijkstra ([3a2a24f](https://github.com/amejiarosario/dsa.js/commit/3a2a24ffae2af5e5e348c237195f7a39717ae617)) +* **heap:** add error handling for heaps ([827177f](https://github.com/amejiarosario/dsa.js/commit/827177f6023f639db0c2cc267dceb0a27746038a)) +* **pq:** improves docs and usability of priority queues ([edf8808](https://github.com/amejiarosario/dsa.js/commit/edf8808970d57aaf397958d2cab1a6cc2e029d26)) + + +### BREAKING CHANGES + +* **heap:** size is now an attribute rather than a method. Similar to the built-in Map.size and Set.size + # [1.18.0](https://github.com/amejiarosario/dsa.js/compare/1.17.0...1.18.0) (2020-09-07) diff --git a/book/interview-questions/network-delay-time.js b/book/interview-questions/network-delay-time.js index 99f8c108..97a00c32 100644 --- a/book/interview-questions/network-delay-time.js +++ b/book/interview-questions/network-delay-time.js @@ -1,28 +1,57 @@ -// https://leetcode.com/problems/network-delay-time/solution/ -function networkDelayTime(times: number[][], N: number, K: number): number { - const graph = new Map(Array(N).fill(0).map((_, i) => [i + 1, []])); - times.forEach(([u, v, w]) => graph.get(u)?.push([v, w])); +const { PriorityQueue, Queue } = require('../../src/index'); - const queue = new Queue([[K, 0]]); - const seen = Array(N + 1).fill(Infinity); +// tag::description[] +function networkDelayTime(times, N, K) { + // end::description[] + // tag::placeholder[] + // write your code here... + // end::placeholder[] + // tag::solution[] + const graph = new Map(Array(N).fill(0).map((_, i) => [i + 1, []])); + times.forEach(([u, v, w]) => graph.get(u).push([v, w])); - while (queue.size()) { - const [node, dist] = queue.dequeue(); - seen[node] = Math.min(seen[node], dist); + const q = new PriorityQueue([[0, K]]); + const dist = new Map(); - for (const [adj, w] of graph.get(node) || []) { - if (seen[adj] > dist + w) queue.enqueue([adj, dist + w]); + while (q.size) { + const [d, n] = q.dequeue(); + + if (dist.has(n)) continue; + dist.set(n, d); + + for (const [adj, w] of graph.get(n)) { + if (!dist.has(adj)) q.enqueue([d + w, adj]); } } - const max = Math.max(...seen.slice(1)); - return max === Infinity ? -1 : max; -}; + return dist.size === N ? Math.max(...dist.values()) : -1; + // end::solution[] + // tag::description[] +} +// end::description[] + +// tag::networkDelayTimeQueue[] +function networkDelayTimeQueue(times, N, K) { + const graph = new Map(Array(N).fill(0).map((_, i) => [i + 1, []])); + times.forEach(([u, v, w]) => graph.get(u).push([v, w])); + + const q = new Queue([[0, K]]); + const dist = new Map(); -/* -[[2,1,1],[2,3,1],[3,4,1]] -4 -2 + while (q.size) { + const [d, n] = q.dequeue(); + + dist.set(n, dist.has(n) ? Math.min(dist.get(n), d) : d); + + for (const [adj, w] of graph.get(n)) { + if (!dist.has(adj) || dist.get(adj) > d + w) { + q.enqueue([d + w, adj]); + } + } + } + return dist.size === N ? Math.max(...dist.values()) : -1; +} +// end::networkDelayTimeQueue[] -*/ +module.exports = { networkDelayTime, networkDelayTimeQueue }; diff --git a/book/interview-questions/network-delay-time.spec.js b/book/interview-questions/network-delay-time.spec.js index c56ff203..24563b4e 100644 --- a/book/interview-questions/network-delay-time.spec.js +++ b/book/interview-questions/network-delay-time.spec.js @@ -1,5 +1,48 @@ -describe('', () => { - it('', () => { +const { networkDelayTime, networkDelayTimeQueue } = require('./network-delay-time'); +[networkDelayTime, networkDelayTimeQueue].forEach((fn) => { + describe(`Graph/PriorityQueue: ${fn.name}`, () => { + it('should work with simple case', () => { + const times = [[2, 1, 1], [2, 3, 1], [3, 4, 1]]; + const n = 4; + const k = 2; + expect(fn(times, n, k)).toEqual(2); + }); + + fit('should work with loops', () => { + const times = [[1, 2, 0], [1, 5, 10], [1, 4, 1], [2, 3, 100], [4, 5, 1], [5, 6, 1], [6, 7, 1], [7, 3, 1], [7, 5, 1]]; + const n = 7; + const k = 1; + expect(fn(times, n, k)).toEqual(5); + }); + + fit('should work with loops and dead starts', () => { + const times = [[1, 2, 0], [1, 5, 10], [1, 4, 1], [2, 3, 100], [4, 5, 1], [5, 6, 1], [6, 7, 1], [7, 3, 1], [7, 5, 1]]; + const n = 7; + const k = 3; + expect(fn(times, n, k)).toEqual(-1); + }); + + it('should work', () => { + const times = [[2, 1, 15], [2, 3, 85], [1, 3, 0], [1, 2, 91], [3, 2, 78], [3, 1, 36]]; + const n = 3; + const k = 2; + expect(fn(times, n, k)).toEqual(15); + }); + + it('should work with highly connected networks', () => { + const times = [[14, 1, 8], [11, 2, 25], [14, 15, 37], [3, 7, 70], [11, 7, 60], [13, 11, 87], [15, 10, 67], [13, 10, 58], [5, 4, 56], [9, 3, 26], [5, 11, 51], [11, 4, 92], [7, 6, 8], [7, 10, 95], [14, 9, 0], [4, 13, 1], [7, 9, 89], [3, 14, 24], [11, 15, 30], [13, 2, 91], [15, 8, 60], [1, 4, 96], [8, 2, 71], [6, 8, 38], [14, 13, 46], [2, 12, 48], [10, 11, 92], [8, 12, 28], [8, 7, 12], [9, 13, 82], [8, 6, 27], [3, 2, 65], [4, 10, 62], [11, 13, 55], [1, 2, 52], [8, 3, 98], [7, 12, 85], [6, 12, 97], [9, 4, 90], [2, 4, 23], [9, 11, 20], [1, 14, 61], [8, 9, 77], [6, 5, 80], [14, 11, 33], [9, 8, 54], [13, 1, 42], [13, 8, 13], [10, 14, 40], [9, 7, 18], [14, 3, 50], [14, 6, 83], [14, 8, 14], [2, 1, 86], [9, 5, 54], [11, 5, 29], [9, 12, 43], [9, 2, 74], [14, 4, 87], [12, 7, 98], [7, 14, 13], [4, 12, 33], [5, 2, 60], [15, 11, 33], [8, 4, 99], [9, 6, 98], [4, 6, 57], [6, 11, 5], [9, 15, 37], [1, 3, 30], [9, 10, 60], [13, 12, 73], [13, 14, 56], [1, 11, 13], [14, 2, 8], [4, 15, 60], [11, 3, 90], [2, 5, 86], [11, 1, 1], [13, 4, 2], [15, 7, 91], [15, 4, 51], [11, 6, 70], [2, 7, 51], [11, 9, 37], [4, 2, 92], [10, 4, 4], [7, 2, 30], [13, 9, 79], [8, 15, 41], [11, 8, 18], [15, 2, 4], [12, 14, 88], [12, 6, 9], [12, 9, 44], [1, 6, 87], [15, 14, 42], [4, 9, 41], [7, 15, 90], [4, 1, 84], [7, 11, 9], [3, 11, 75], [5, 9, 2], [2, 11, 96], [12, 5, 89], [6, 15, 25], [5, 13, 7], [15, 5, 32], [13, 5, 84], [7, 5, 9], [15, 3, 14], [12, 13, 4], [5, 3, 73], [6, 9, 85], [6, 10, 29], [1, 8, 24], [12, 3, 85], [4, 3, 60], [1, 13, 6], [1, 5, 58], [2, 3, 29], [14, 5, 67], [13, 15, 70], [5, 14, 94], [15, 1, 95], [3, 1, 17], [10, 2, 6], [11, 10, 44], [9, 14, 62], [4, 11, 32], [15, 13, 48], [2, 10, 77], [3, 13, 90], [5, 7, 68], [10, 6, 78], [3, 6, 95], [10, 12, 68], [13, 6, 73], [10, 1, 8], [10, 7, 18], [10, 5, 64], [5, 1, 55], [13, 7, 90], [1, 9, 67], [3, 12, 76], [14, 10, 22], [12, 8, 83], [4, 7, 76], [8, 13, 25], [5, 6, 57], [13, 3, 90], [6, 2, 96], [11, 14, 61], [12, 1, 94], [12, 15, 12], [4, 8, 88], [4, 14, 27], [7, 4, 25], [3, 9, 57], [2, 15, 90], [1, 12, 85], [12, 11, 44], [5, 10, 13], [5, 12, 96], [14, 7, 24], [14, 12, 98], [10, 9, 36], [15, 6, 17], [8, 10, 11], [2, 13, 5], [10, 3, 78], [6, 13, 11], [5, 15, 34], [12, 10, 12], [9, 1, 68], [10, 13, 1], [7, 13, 86], [1, 7, 62], [2, 14, 53], [8, 14, 75], [2, 6, 49], [10, 15, 83], [7, 8, 88], [6, 1, 87], [8, 1, 38], [8, 11, 73], [3, 15, 1], [3, 8, 93], [2, 8, 26], [4, 5, 26], [3, 4, 58], [7, 1, 55], [7, 3, 84], [5, 8, 97], [12, 4, 42], [6, 3, 71], [6, 7, 48], [15, 12, 3], [1, 15, 30], [10, 8, 11], [2, 9, 49], [6, 14, 95], [3, 10, 68], [6, 4, 14], [11, 12, 29], [1, 10, 93], [8, 5, 55], [12, 2, 86], [3, 5, 26], [15, 9, 12]]; + const n = 15; + const k = 11; + expect(fn(times, n, k)).toEqual(38); + }); + + it('should work with highly connected networks', () => { + const times = [[15, 8, 1], [7, 10, 41], [7, 9, 34], [9, 4, 31], [12, 13, 50], [14, 3, 52], [4, 11, 99], [4, 7, 86], [10, 13, 57], [9, 6, 10], [1, 7, 51], [7, 15, 38], [1, 9, 11], [12, 7, 94], [9, 13, 34], [11, 7, 79], [7, 6, 28], [5, 3, 34], [2, 6, 97], [14, 1, 97], [6, 10, 90], [12, 10, 37], [13, 3, 73], [11, 14, 7], [15, 1, 39], [6, 5, 90], [13, 6, 43], [6, 9, 32], [4, 6, 45], [11, 10, 2], [2, 13, 4], [14, 15, 29], [1, 14, 88], [14, 6, 19], [6, 2, 29], [3, 14, 72], [1, 15, 4], [11, 5, 2], [6, 7, 56], [8, 7, 88], [13, 14, 70], [14, 12, 58], [14, 2, 86], [11, 3, 57], [5, 2, 56], [3, 10, 26], [2, 11, 21], [14, 5, 54], [5, 12, 40], [14, 4, 81], [15, 2, 99], [5, 7, 57], [13, 12, 5], [4, 9, 60], [12, 15, 48], [6, 14, 1], [9, 7, 44], [13, 7, 69], [5, 13, 42], [4, 1, 7], [11, 9, 76], [8, 1, 76], [5, 14, 29], [2, 3, 69], [7, 3, 23], [12, 14, 28], [11, 4, 85], [10, 1, 10], [15, 12, 36], [1, 11, 69], [15, 10, 96], [11, 13, 69], [7, 12, 49], [1, 2, 95], [6, 4, 46], [8, 12, 94], [12, 4, 93], [13, 5, 31], [12, 2, 60], [6, 1, 87], [4, 14, 20], [5, 11, 89], [4, 15, 88], [4, 10, 21], [1, 6, 5], [10, 8, 26], [8, 2, 51], [3, 15, 23], [7, 2, 12], [11, 1, 47], [2, 1, 75], [3, 8, 63], [8, 10, 19], [6, 8, 18], [4, 2, 55], [14, 11, 80], [10, 3, 73], [3, 5, 22], [12, 3, 61], [1, 13, 33], [9, 3, 98], [9, 12, 69], [15, 9, 6], [7, 13, 76], [11, 12, 22], [11, 15, 51], [13, 15, 46], [5, 10, 58], [1, 10, 26], [13, 4, 85], [7, 14, 58], [5, 8, 46], [11, 6, 32], [10, 9, 41], [9, 14, 35], [14, 13, 60], [3, 9, 97], [2, 5, 39], [7, 11, 19], [1, 12, 27], [7, 5, 13], [8, 4, 34], [9, 15, 25], [5, 1, 93], [15, 13, 97], [14, 9, 35], [8, 6, 67], [9, 5, 39], [13, 11, 35], [7, 4, 21], [12, 9, 64], [14, 8, 8], [10, 12, 94], [8, 9, 76], [8, 5, 71], [2, 9, 64], [10, 14, 59], [1, 4, 74], [7, 1, 69], [15, 5, 55], [6, 15, 80], [13, 8, 84], [8, 13, 63], [8, 3, 91], [10, 4, 87], [1, 5, 39], [8, 11, 0], [1, 3, 79], [4, 5, 82], [4, 12, 87], [3, 11, 29], [7, 8, 92], [10, 7, 77], [6, 12, 42], [13, 2, 40], [9, 10, 13], [4, 13, 65], [2, 4, 34], [3, 13, 44], [2, 14, 69], [3, 4, 42], [5, 15, 98], [14, 7, 6], [15, 3, 94], [10, 2, 37], [15, 11, 7], [9, 2, 15], [13, 9, 66], [4, 8, 83], [8, 15, 23], [13, 1, 50], [6, 13, 57], [2, 10, 37], [10, 6, 38], [2, 7, 45], [9, 8, 8], [3, 12, 28], [3, 2, 83], [2, 12, 75], [1, 8, 91], [4, 3, 70], [12, 6, 48], [3, 1, 13], [5, 6, 42], [6, 11, 96], [3, 6, 22], [15, 6, 34], [11, 8, 43], [15, 7, 40], [9, 11, 57], [11, 2, 11], [2, 8, 22], [9, 1, 73], [2, 15, 40], [12, 11, 10], [15, 4, 78], [12, 8, 75], [10, 15, 37], [13, 10, 44], [8, 14, 33], [3, 7, 82], [5, 4, 46], [12, 5, 79], [15, 14, 43], [10, 5, 65], [5, 9, 34], [12, 1, 54], [6, 3, 16], [14, 10, 83], [10, 11, 67]]; + const n = 15; + const k = 8; + expect(fn(times, n, k)).toEqual(34); + }); }); }); + diff --git a/package-lock.json b/package-lock.json index bf7a41de..5b36d0f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "dsa.js", - "version": "1.18.0", + "version": "2.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 5e130a84..bba73b72 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dsa.js", - "version": "1.18.0", + "version": "2.0.0", "description": "Data Structures & Algorithms in JS", "author": "Adrian Mejia (https://adrianmejia.com)", "homepage": "https://github.com/amejiarosario/dsa.js", diff --git a/src/data-structures/heaps/heap.js b/src/data-structures/heaps/heap.js index 459985dc..54863c39 100644 --- a/src/data-structures/heaps/heap.js +++ b/src/data-structures/heaps/heap.js @@ -8,7 +8,11 @@ class Heap { constructor(comparator = (a, b) => a - b) { this.array = []; - this.comparator = (i1, i2) => comparator(this.array[i1], this.array[i2]); + this.comparator = (i1, i2) => { + const value = comparator(this.array[i1], this.array[i2]); + if (Number.isNaN(value)) { throw new Error(`Comparator should evaluate to a number. Got ${value} when comparing ${this.array[i1]} with ${this.array[i2]}`); } + return value; + }; } /** @@ -34,8 +38,8 @@ class Heap { * @runtime O(log n) */ remove(index = 0) { - if (!this.size()) return null; - this.swap(index, this.size() - 1); // swap with last + if (!this.size) return null; + this.swap(index, this.size - 1); // swap with last const value = this.array.pop(); // remove element this.bubbleDown(index); return value; @@ -45,7 +49,7 @@ class Heap { * Returns the number of elements in this collection. * @runtime O(1) */ - size() { + get size() { return this.array.length; } @@ -54,7 +58,7 @@ class Heap { * @runtime O(log n) */ bubbleUp() { - let index = this.size() - 1; + let index = this.size - 1; const parent = (i) => Math.ceil(i / 2 - 1); while (parent(index) >= 0 && this.comparator(parent(index), index) > 0) { this.swap(parent(index), index); @@ -70,10 +74,10 @@ class Heap { let curr = index; const left = (i) => 2 * i + 1; const right = (i) => 2 * i + 2; - const getTopChild = (i) => (right(i) < this.size() + const getTopChild = (i) => (right(i) < this.size && this.comparator(left(i), right(i)) > 0 ? right(i) : left(i)); - while (left(curr) < this.size() && this.comparator(curr, getTopChild(curr)) > 0) { + while (left(curr) < this.size && this.comparator(curr, getTopChild(curr)) > 0) { const next = getTopChild(curr); this.swap(curr, next); curr = next; @@ -81,7 +85,7 @@ class Heap { } /** - * "Private": Swap elements on the heap + * Swap elements on the heap * @runtime O(1) * @param {number} i1 index 1 * @param {number} i2 index 2 diff --git a/src/data-structures/heaps/heap.spec.js b/src/data-structures/heaps/heap.spec.js index 855bc6cf..f1d6bb09 100644 --- a/src/data-structures/heaps/heap.spec.js +++ b/src/data-structures/heaps/heap.spec.js @@ -3,12 +3,16 @@ const PriorityQueue = require('./priority-queue'); const MaxHeap = require('./max-heap'); const MinHeap = require('./min-heap'); -[[Heap], [PriorityQueue], [MinHeap]].forEach(([DS, arg]) => { - describe('Min-Heap (Priority Queue)', () => { +[ + [Heap], + [PriorityQueue, [], (a, b) => a - b], + [MinHeap], +].forEach(([DS, ...arg]) => { + describe('Min-Heap and Priority Queue', () => { let heap; beforeEach(() => { - heap = new DS(arg); + heap = new DS(...arg); }); describe('#contructor', () => { @@ -21,7 +25,7 @@ const MinHeap = require('./min-heap'); it('should add an element', () => { expect(heap.add(1)).toBe(undefined); expect(heap.array).toEqual([1]); - expect(heap.size()).toBe(1); + expect(heap.size).toBe(1); }); it('should keep things in order', () => { @@ -31,7 +35,7 @@ const MinHeap = require('./min-heap'); expect(heap.array[0]).toEqual(2); heap.add(1); expect(heap.array[0]).toEqual(1); - expect(heap.size()).toEqual(3); + expect(heap.size).toEqual(3); }); }); @@ -40,7 +44,7 @@ const MinHeap = require('./min-heap'); heap.add(1); heap.add(0); expect(heap.remove()).toBe(0); - expect(heap.size()).toBe(1); + expect(heap.size).toBe(1); expect(heap.array).toEqual([1]); }); @@ -70,19 +74,23 @@ const MinHeap = require('./min-heap'); expect(heap.remove()).toEqual(1); expect(heap.remove()).toEqual(2); expect(heap.remove()).toEqual(3); - expect(heap.size()).toBe(0); + expect(heap.size).toBe(0); }); }); }); }); }); -[[Heap, (a, b) => b - a], [PriorityQueue, (a, b) => b - a], [MaxHeap]].forEach(([DS, arg]) => { +[ + [Heap, (a, b) => b - a], + [PriorityQueue, [], (a, b) => b - a], + [MaxHeap], +].forEach(([DS, ...arg]) => { describe('Max-Heap (Priority Queue)', () => { let heap; beforeEach(() => { - heap = new DS(arg); + heap = new DS(...arg); }); describe('#contructor', () => { @@ -95,7 +103,7 @@ const MinHeap = require('./min-heap'); it('should add an element', () => { expect(heap.add(1)).toBe(undefined); expect(heap.array).toEqual([1]); - expect(heap.size()).toBe(1); + expect(heap.size).toBe(1); }); it('should keep things in order', () => { @@ -105,7 +113,7 @@ const MinHeap = require('./min-heap'); expect(heap.array[0]).toEqual(2); heap.add(3); expect(heap.array[0]).toEqual(3); - expect(heap.size()).toEqual(3); + expect(heap.size).toEqual(3); }); }); @@ -114,7 +122,7 @@ const MinHeap = require('./min-heap'); heap.add(1); heap.add(0); expect(heap.remove()).toBe(1); - expect(heap.size()).toBe(1); + expect(heap.size).toBe(1); expect(heap.array).toEqual([0]); }); @@ -156,7 +164,7 @@ const MinHeap = require('./min-heap'); expect(heap.remove()).toEqual(2); expect(heap.remove()).toEqual(1); expect(heap.remove()).toEqual(0); - expect(heap.size()).toBe(0); + expect(heap.size).toBe(0); }); }); }); diff --git a/src/data-structures/heaps/median-heap.js b/src/data-structures/heaps/median-heap.js index 75c29b79..e8295963 100644 --- a/src/data-structures/heaps/median-heap.js +++ b/src/data-structures/heaps/median-heap.js @@ -30,9 +30,9 @@ class MedianHeap { } // rebalance if the sizes of the heaps differ by more than one element - if (Math.abs(this.min.size() - this.max.size()) > 1) { + if (Math.abs(this.min.size - this.max.size) > 1) { // extract the min/max from the heap with more elements and insert it into the other heap. - if (this.min.size() > this.max.size()) { + if (this.min.size > this.max.size) { this.max.add(this.min.remove()); } else { this.min.add(this.max.remove()); @@ -47,12 +47,12 @@ class MedianHeap { findMedian() { let median; - if (this.max.size() === this.min.size()) { + if (this.max.size === this.min.size) { // When both heaps contain the same number of elements, // the total number of elements is even. // The median is the mean of the two middle elements. median = (this.max.peek() + this.min.peek()) / 2; - } else if (this.max.size() > this.min.size()) { + } else if (this.max.size > this.min.size) { // when the max-heap contains one more element than the min-heap, // the median is in the top of the max-heap. median = this.max.peek(); @@ -67,8 +67,8 @@ class MedianHeap { /** * Return size of the heap. */ - size() { - return this.min.size() + this.max.size(); + get size() { + return this.min.size + this.max.size; } } diff --git a/src/data-structures/heaps/median-heap.spec.js b/src/data-structures/heaps/median-heap.spec.js index 283df75d..5f6de956 100644 --- a/src/data-structures/heaps/median-heap.spec.js +++ b/src/data-structures/heaps/median-heap.spec.js @@ -10,13 +10,13 @@ describe('Median Heap', () => { describe('#add', () => { it('should work', () => { expect(medianHeap.add(1)).toEqual(undefined); - expect(medianHeap.size()).toEqual(1); + expect(medianHeap.size).toEqual(1); }); it('should work', () => { expect(medianHeap.add(1)).toEqual(undefined); expect(medianHeap.add(1)).toEqual(undefined); - expect(medianHeap.size()).toEqual(2); + expect(medianHeap.size).toEqual(2); }); }); diff --git a/src/data-structures/heaps/priority-queue.js b/src/data-structures/heaps/priority-queue.js index 622af6a9..b81772e8 100644 --- a/src/data-structures/heaps/priority-queue.js +++ b/src/data-structures/heaps/priority-queue.js @@ -1,5 +1,28 @@ const Heap = require('./heap'); -class PriorityQueue extends Heap { } +class PriorityQueue extends Heap { + constructor(iterable = [], comparator = (a, b) => a[0] - b[0]) { + super(comparator); + Array.from(iterable).forEach((el) => this.add(el)); + } + + /** + * Add data to the Queue with Priority + * @param {[number, any]|any} value - Pair with [priority, value] + * any object as value is also possible if a custom comparator is passed in. + * @returns {void} + */ + enqueue(value) { + super.add(value); + } + + /** + * Remove from the queue the element with the highest priority. + * @returns {[number, any]|any} + */ + dequeue() { + return super.remove(); + } +} module.exports = PriorityQueue; diff --git a/src/data-structures/heaps/priority-queue.spec.js b/src/data-structures/heaps/priority-queue.spec.js new file mode 100644 index 00000000..860480da --- /dev/null +++ b/src/data-structures/heaps/priority-queue.spec.js @@ -0,0 +1,75 @@ +const { PriorityQueue } = require('../..'); + +describe('Priorty Queue (as MinHeap default)', () => { + const num = 1; + const obj = { a: 1, b: 2 }; + let pq; + + describe('with default contructor', () => { + beforeEach(() => { + pq = new PriorityQueue(); + }); + + describe('.enqueue', () => { + it('should enqueue [priority, element]', () => { + pq.enqueue([Infinity, 2]); + pq.enqueue([0, 1]); + pq.enqueue([100, { a: 1, b: 2 }]); + expect(pq.size).toEqual(3); + expect(pq.peek()).toEqual([0, 1]); + }); + }); + + describe('.dequeue', () => { + it('should enqueue and dequeue elements on priority order', () => { + pq.enqueue([100, obj]); + pq.enqueue([Infinity, 2]); + pq.enqueue([0, num]); + + expect(pq.dequeue()).toEqual([0, num]); + expect(pq.size).toEqual(2); + expect(pq.dequeue()).toEqual([100, obj]); + expect(pq.dequeue()).toEqual([Infinity, 2]); + expect(pq.size).toEqual(0); + }); + + it('should handle case when priorty was forgotten', () => { + expect(() => pq.enqueue({ a: 100 })).not.toThrow(); + expect(() => pq.enqueue({ b: 200 })).toThrow(); + }); + }); + }); + + describe('with default values', () => { + it('should add values on creation', () => { + pq = new PriorityQueue([[100, obj], [Infinity, 2], [0, num]]); + expect(pq.size).toEqual(3); + expect(pq.peek()).toEqual([0, num]); + expect(pq.dequeue()).toEqual([0, num]); + expect(pq.size).toEqual(2); + }); + }); + + describe('with custom comparator', () => { + const alice = { name: 'Alice', grade: 80, assistance: 1 }; + const bob = { name: 'Bob', grade: 93, assistance: 0.7 }; + const ana = { name: 'Ana', grade: 98, assistance: 0.8 }; + + it('should become MaxPriortyQueue and compare objects', () => { + pq = new PriorityQueue([], (a, b) => b.grade * b.assistance - a.grade * a.assistance); + pq.enqueue(alice); + pq.enqueue(ana); + pq.enqueue(bob); + expect(pq.size).toEqual(3); + expect(pq.dequeue()).toEqual(alice); + expect(pq.dequeue()).toEqual(ana); + expect(pq.dequeue()).toEqual(bob); + }); + + it('should handle errors', () => { + pq = new PriorityQueue([], (a, b) => b.grade - a.grade); + expect(() => pq.enqueue(alice)).not.toThrow(); + expect(() => pq.enqueue({ name: 'Oops', error: 98 })).toThrow(); + }); + }); +});