From 8b5247a3a7abf5c04432a9859e08e158fa4fd478 Mon Sep 17 00:00:00 2001 From: Pauline Garelli Date: Wed, 22 Feb 2023 08:52:42 +0100 Subject: [PATCH 1/8] ref: KeyPriorityQueue in separate file #1298 --- Data-Structures/Heap/KeyPriorityQueue.js | 185 +++++++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 Data-Structures/Heap/KeyPriorityQueue.js diff --git a/Data-Structures/Heap/KeyPriorityQueue.js b/Data-Structures/Heap/KeyPriorityQueue.js new file mode 100644 index 0000000000..2cf7e4c496 --- /dev/null +++ b/Data-Structures/Heap/KeyPriorityQueue.js @@ -0,0 +1,185 @@ +/** + * KeyPriorityQueue is a priority queue based on a Minimum Binary Heap. + * + * Minimum Binary Heaps are binary trees which are filled level by level + * and then from left to right inside a depth level. + * Their main property is that any parent node has a smaller or equal priority to all of its children, + * hence the root of the tree always has the smallest priority of all nodes. + * + * This implementation of the Minimum Binary Heap allows for nodes to contain both a key and a priority. + * WARNING: keys must be Integers as they are used as array indices. + * + * In this implementation, the heap is represented by an array with nodes ordered + * from root-to-leaf, left-to-right. + * Therefore, the parent-child node relationship is such that + * * the children nodes positions relative to their parent are: (parentPos * 2 + 1) and (parentPos * 2 + 2) + * * the parent node position relative to either of its children is: Math.floor((childPos - 1) / 2) + * + * More information and visuals on Binary Heaps can be found here: https://www.geeksforgeeks.org/binary-heap/ + */ + + +// Priority Queue Helper functions +function getParentPosition(position) { + // Get the parent node of the current node + return Math.floor((position - 1) / 2) +} +function getChildrenPosition(position) { + // Get the children nodes of the current node + return [2 * position + 1, 2 * position + 2] +} + +class KeyPriorityQueue { + // Priority Queue class using Minimum Binary Heap + constructor() { + this._heap = [] + this.keys = {} + } + + /** + * Checks if the heap is empty + * @returns boolean + */ + isEmpty() { + return this._heap.length === 0 + } + + /** + * Adds an element to the queue + * @param {number} key + * @param {number} priority + */ + push(key, priority) { + this._heap.push([key, priority]) + this.keys[key] = this._heap.length - 1 + this._shiftUp(this.keys[key]) + } + + /** + * Removes the element with least priority + * @returns the key of the element with least priority + */ + pop() { + this._swap(0, this._heap.length - 1) + const [key] = this._heap.pop() + delete this.keys[key] + this._shiftDown(0) + return key + } + + /** + * Checks whether a given key is present in the queue + * @param {number} key + * @returns boolean + */ + contains(key) { + return (key in this.keys) + } + + /** + * Updates the priority of the given element + * @param {number} key the element to change + * @param {number} priority new priority of the element + */ + update(key, priority) { + const currPos = this.keys[key] + this._heap[currPos][1] = priority + const parentPos = getParentPosition(currPos) + const currPriority = this._heap[currPos][1] + let parentPriority = Infinity + if (parentPos >= 0) { + parentPriority = this._heap[parentPos][1] + } + const [child1Pos, child2Pos] = getChildrenPosition(currPos) + let [child1Priority, child2Priority] = [Infinity, Infinity] + if (child1Pos < this._heap.length) { + child1Priority = this._heap[child1Pos][1] + } + if (child2Pos < this._heap.length) { + child2Priority = this._heap[child2Pos][1] + } + + if (parentPos >= 0 && parentPriority > currPriority) { + this._shiftUp(currPos) + } else if (child2Pos < this._heap.length && + (child1Priority < currPriority || child2Priority < currPriority)) { + this._shiftDown(currPos) + } + } + + _shiftUp(position) { + // Helper function to shift up a node to proper position (equivalent to bubbleUp) + let currPos = position + let parentPos = getParentPosition(currPos) + let currPriority = this._heap[currPos][1] + let parentPriority = Infinity + if (parentPos >= 0) { + parentPriority = this._heap[parentPos][1] + } + + while (parentPos >= 0 && parentPriority > currPriority) { + this._swap(currPos, parentPos) + currPos = parentPos + parentPos = getParentPosition(currPos) + currPriority = this._heap[currPos][1] + try { + parentPriority = this._heap[parentPos][1] + } catch (error) { + parentPriority = Infinity + } + } + this.keys[this._heap[currPos][0]] = currPos + } + + _shiftDown(position) { + // Helper function to shift down a node to proper position (equivalent to bubbleDown) + let currPos = position + let [child1Pos, child2Pos] = getChildrenPosition(currPos) + let [child1Priority, child2Priority] = [Infinity, Infinity] + if (child1Pos < this._heap.length) { + child1Priority = this._heap[child1Pos][1] + } + if (child2Pos < this._heap.length) { + child2Priority = this._heap[child2Pos][1] + } + let currPriority + try { + currPriority = this._heap[currPos][1] + } catch { + return + } + + while (child2Pos < this._heap.length && + (child1Priority < currPriority || child2Priority < currPriority)) { + if (child1Priority < currPriority && child1Priority < child2Priority) { + this._swap(child1Pos, currPos) + currPos = child1Pos + } else { + this._swap(child2Pos, currPos) + currPos = child2Pos + } + [child1Pos, child2Pos] = getChildrenPosition(currPos) + try { + [child1Priority, child2Priority] = [this._heap[child1Pos][1], this._heap[child2Pos][1]] + } catch (error) { + [child1Priority, child2Priority] = [Infinity, Infinity] + } + + currPriority = this._heap[currPos][1] + } + this.keys[this._heap[currPos][0]] = currPos + if (child1Pos < this._heap.length && child1Priority < currPriority) { + this._swap(child1Pos, currPos) + this.keys[this._heap[child1Pos][0]] = child1Pos + } + } + + _swap(position1, position2) { + // Helper function to swap 2 nodes + [this._heap[position1], this._heap[position2]] = [this._heap[position2], this._heap[position1]] + this.keys[this._heap[position1][0]] = position1 + this.keys[this._heap[position2][0]] = position2 + } +} + +export { KeyPriorityQueue } \ No newline at end of file From ffdd09a465e627c9af4f405b52eea0588ad5a787 Mon Sep 17 00:00:00 2001 From: Pauline Garelli Date: Wed, 22 Feb 2023 10:21:43 +0100 Subject: [PATCH 2/8] feat: add tests for KeyPriorityQueue #1298 --- .../Heap/test/KeyPriorityQueue.test.js | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 Data-Structures/Heap/test/KeyPriorityQueue.test.js diff --git a/Data-Structures/Heap/test/KeyPriorityQueue.test.js b/Data-Structures/Heap/test/KeyPriorityQueue.test.js new file mode 100644 index 0000000000..07846be0c5 --- /dev/null +++ b/Data-Structures/Heap/test/KeyPriorityQueue.test.js @@ -0,0 +1,148 @@ +import { KeyPriorityQueue } from '../KeyPriorityQueue.js' + +describe('KeyPriorityQueue', () => { + + test('Check heap correctly ordered', () => { + // create queue and fill it + const priorities = [5, 2, 4, 1, 7, 6, 3, 8] + const queue = new KeyPriorityQueue() + for (let i = 0; i < priorities.length; i++) { + queue.push(i, priorities[i]) + } + + // result from popping all elements from the queue + let res = [] + while (!queue.isEmpty()) { + res.push(queue.pop()) + } + + expect(res).toEqual([1, 2, 3, 5, 7, 6, 4, 8]) + }) +}) + +describe('Method isEmpty', () => { + + test('Check heap is empty', () => { + const queue = new KeyPriorityQueue() + let res = queue.isEmpty() + expect(res).toEqual(true) + }) + + test('Check heap is not empty', () => { + const queue = new KeyPriorityQueue() + queue.push(0, 2) + let res = queue.isEmpty() + expect(res).toEqual(false) + }) +}) + +describe('Methods push and pop', () => { + + test('Test Case 1', () => { + // create queue + const queue = new KeyPriorityQueue() + queue.push(0, 3) + queue.push(1, 7) + queue.push(2, 9) + queue.push(3, 13) + // create expected queue + const expectedQueue = new KeyPriorityQueue() + expectedQueue.push(1, 7) + expectedQueue.push(3, 13) + expectedQueue.push(2, 9) + // result from popping element from the queue + queue.pop() + expect(queue).toEqual(expectedQueue) + }) + + test('Test Case 2', () => { + // create queue + const queue = new KeyPriorityQueue() + queue.push(0, 3) + queue.push(1, 9) + queue.push(2, 7) + queue.push(3, 13) + // create expected queue + const expectedQueue = new KeyPriorityQueue() + expectedQueue.push(2, 7) + expectedQueue.push(1, 9) + expectedQueue.push(3, 13) + // result from popping element from the queue + queue.pop() + expect(queue).toEqual(expectedQueue) + }) + + test('Test Case 3', () => { + // create queue + const queue = new KeyPriorityQueue() + queue.push(0, 3) + queue.push(1, 7) + queue.push(2, 9) + queue.push(3, 12) + queue.push(4, 13) + // create expected queue + const expectedQueue = new KeyPriorityQueue() + expectedQueue.push(1, 7) + expectedQueue.push(3, 12) + expectedQueue.push(2, 9) + expectedQueue.push(4, 13) + // result from popping element from the queue + queue.pop() + expect(queue).toEqual(expectedQueue) + }) +}) + +describe('Method contains', () => { + + test('Check heap does not contain element', () => { + const queue = new KeyPriorityQueue() + let res = queue.contains(0) + expect(res).toEqual(false) + }) + + test('Check heap contains element', () => { + const queue = new KeyPriorityQueue() + queue.push(0, 2) + let res = queue.contains(0) + expect(res).toEqual(true) + }) +}) + +describe('Method update', () => { + + test('Update without change in position', () => { + // create queue + const queue = new KeyPriorityQueue() + queue.push(0, 3) + queue.push(1, 5) + queue.push(2, 7) + queue.push(3, 11) + // create expected queue + const expectedQueue = new KeyPriorityQueue() + expectedQueue.push(0, 3) + expectedQueue.push(1, 5) + expectedQueue.push(2, 7) + expectedQueue.push(3, 11) + // result from updating to similar priority + queue.update(0, 2) + expect(queue).toEqual(expectedQueue) + }) + + test('Update with change in position', () => { + // create queue + const queue = new KeyPriorityQueue() + queue.push(0, 3) + queue.push(1, 5) + queue.push(2, 7) + queue.push(3, 11) + // create expected queue + const expectedQueue = new KeyPriorityQueue() + expectedQueue.push(1, 5) + expectedQueue.push(3, 11) + expectedQueue.push(2, 7) + expectedQueue.push(0, 9) + // result from updating to similar priority + queue.update(0, 9) + expect(queue).toEqual(expectedQueue) + }) +}) \ No newline at end of file From a69914dfe9a41b1fd13804e59fa2861d2d40fbee Mon Sep 17 00:00:00 2001 From: Pauline Garelli Date: Wed, 22 Feb 2023 10:43:32 +0100 Subject: [PATCH 3/8] fix: _shiftDown refactored and corrected #1298 --- Data-Structures/Heap/KeyPriorityQueue.js | 79 +++++++------------ .../Heap/test/KeyPriorityQueue.test.js | 22 +----- 2 files changed, 30 insertions(+), 71 deletions(-) diff --git a/Data-Structures/Heap/KeyPriorityQueue.js b/Data-Structures/Heap/KeyPriorityQueue.js index 2cf7e4c496..674b74a7cf 100644 --- a/Data-Structures/Heap/KeyPriorityQueue.js +++ b/Data-Structures/Heap/KeyPriorityQueue.js @@ -85,48 +85,43 @@ class KeyPriorityQueue { const currPos = this.keys[key] this._heap[currPos][1] = priority const parentPos = getParentPosition(currPos) - const currPriority = this._heap[currPos][1] - let parentPriority = Infinity - if (parentPos >= 0) { - parentPriority = this._heap[parentPos][1] - } + const currPriority = this._getPriorityOrInfinite(currPos) + const parentPriority = this._getPriorityOrInfinite(parentPos) const [child1Pos, child2Pos] = getChildrenPosition(currPos) - let [child1Priority, child2Priority] = [Infinity, Infinity] - if (child1Pos < this._heap.length) { - child1Priority = this._heap[child1Pos][1] - } - if (child2Pos < this._heap.length) { - child2Priority = this._heap[child2Pos][1] - } + const child1Priority = this._getPriorityOrInfinite(child1Pos) + const child2Priority = this._getPriorityOrInfinite(child2Pos) if (parentPos >= 0 && parentPriority > currPriority) { this._shiftUp(currPos) - } else if (child2Pos < this._heap.length && - (child1Priority < currPriority || child2Priority < currPriority)) { + } else if (child1Priority < currPriority || child2Priority < currPriority) { this._shiftDown(currPos) } } + _getPriorityOrInfinite (position) { + // Helper function, returns priority of the node, or Infinite if no node corresponds to this position + if (position >= 0 && position < this._heap.length) { + return this._heap[position][1] + } + else { + return Infinity + } + } + _shiftUp(position) { // Helper function to shift up a node to proper position (equivalent to bubbleUp) let currPos = position let parentPos = getParentPosition(currPos) - let currPriority = this._heap[currPos][1] - let parentPriority = Infinity - if (parentPos >= 0) { - parentPriority = this._heap[parentPos][1] - } + let currPriority = this._getPriorityOrInfinite(currPos) + let parentPriority = this._getPriorityOrInfinite(parentPos) while (parentPos >= 0 && parentPriority > currPriority) { this._swap(currPos, parentPos) currPos = parentPos parentPos = getParentPosition(currPos) - currPriority = this._heap[currPos][1] - try { - parentPriority = this._heap[parentPos][1] - } catch (error) { - parentPriority = Infinity - } + currPriority = this._getPriorityOrInfinite(currPos) + parentPriority = this._getPriorityOrInfinite(parentPos) + } this.keys[this._heap[currPos][0]] = currPos } @@ -135,22 +130,15 @@ class KeyPriorityQueue { // Helper function to shift down a node to proper position (equivalent to bubbleDown) let currPos = position let [child1Pos, child2Pos] = getChildrenPosition(currPos) - let [child1Priority, child2Priority] = [Infinity, Infinity] - if (child1Pos < this._heap.length) { - child1Priority = this._heap[child1Pos][1] - } - if (child2Pos < this._heap.length) { - child2Priority = this._heap[child2Pos][1] - } - let currPriority - try { - currPriority = this._heap[currPos][1] - } catch { + let child1Priority = this._getPriorityOrInfinite(child1Pos) + let child2Priority = this._getPriorityOrInfinite(child2Pos) + let currPriority = this._getPriorityOrInfinite(currPos) + + if (currPriority == Infinity) { return } - while (child2Pos < this._heap.length && - (child1Priority < currPriority || child2Priority < currPriority)) { + while (child1Priority < currPriority || child2Priority < currPriority) { if (child1Priority < currPriority && child1Priority < child2Priority) { this._swap(child1Pos, currPos) currPos = child1Pos @@ -159,18 +147,9 @@ class KeyPriorityQueue { currPos = child2Pos } [child1Pos, child2Pos] = getChildrenPosition(currPos) - try { - [child1Priority, child2Priority] = [this._heap[child1Pos][1], this._heap[child2Pos][1]] - } catch (error) { - [child1Priority, child2Priority] = [Infinity, Infinity] - } - - currPriority = this._heap[currPos][1] - } - this.keys[this._heap[currPos][0]] = currPos - if (child1Pos < this._heap.length && child1Priority < currPriority) { - this._swap(child1Pos, currPos) - this.keys[this._heap[child1Pos][0]] = child1Pos + child1Priority = this._getPriorityOrInfinite(child1Pos) + child2Priority = this._getPriorityOrInfinite(child2Pos) + currPriority = this._getPriorityOrInfinite(currPos) } } diff --git a/Data-Structures/Heap/test/KeyPriorityQueue.test.js b/Data-Structures/Heap/test/KeyPriorityQueue.test.js index 07846be0c5..5a726e6cea 100644 --- a/Data-Structures/Heap/test/KeyPriorityQueue.test.js +++ b/Data-Structures/Heap/test/KeyPriorityQueue.test.js @@ -1,25 +1,5 @@ import { KeyPriorityQueue } from '../KeyPriorityQueue.js' -describe('KeyPriorityQueue', () => { - - test('Check heap correctly ordered', () => { - // create queue and fill it - const priorities = [5, 2, 4, 1, 7, 6, 3, 8] - const queue = new KeyPriorityQueue() - for (let i = 0; i < priorities.length; i++) { - queue.push(i, priorities[i]) - } - - // result from popping all elements from the queue - let res = [] - while (!queue.isEmpty()) { - res.push(queue.pop()) - } - - expect(res).toEqual([1, 2, 3, 5, 7, 6, 4, 8]) - }) -}) - describe('Method isEmpty', () => { test('Check heap is empty', () => { @@ -119,7 +99,7 @@ describe('Method update', () => { queue.push(3, 11) // create expected queue const expectedQueue = new KeyPriorityQueue() - expectedQueue.push(0, 3) + expectedQueue.push(0, 2) expectedQueue.push(1, 5) expectedQueue.push(2, 7) expectedQueue.push(3, 11) From 9b55ff4ed18704fff119b3710430ca27986a471e Mon Sep 17 00:00:00 2001 From: Pauline Garelli Date: Wed, 22 Feb 2023 10:46:34 +0100 Subject: [PATCH 4/8] fix: use KeyPriorityQueue in PrimMST #1298 --- Graphs/PrimMST.js | 156 +--------------------------------------------- 1 file changed, 2 insertions(+), 154 deletions(-) diff --git a/Graphs/PrimMST.js b/Graphs/PrimMST.js index 17d68efb90..c02c115fae 100644 --- a/Graphs/PrimMST.js +++ b/Graphs/PrimMST.js @@ -1,148 +1,4 @@ -// Priority Queue Helper functions -function getParentPosition (position) { - // Get the parent node of the current node - return Math.floor((position - 1) / 2) -} -function getChildrenPosition (position) { - // Get the children nodes of the current node - return [2 * position + 1, 2 * position + 2] -} - -class PriorityQueue { - // Priority Queue class using Minimum Binary Heap - constructor () { - this._heap = [] - this.keys = {} - } - - isEmpty () { - // Checking if the heap is empty - return this._heap.length === 0 - } - - push (key, priority) { - // Adding element to the queue (equivalent to add) - this._heap.push([key, priority]) - this.keys[key] = this._heap.length - 1 - this._shiftUp(this.keys[key]) - } - - pop () { - // Removing the element with least priority (equivalent to extractMin) - this._swap(0, this._heap.length - 1) - const [key] = this._heap.pop() - delete this.keys[key] - this._shiftDown(0) - return key - } - - contains (key) { - // Check if a given key is present in the queue - return (key in this.keys) - } - - update (key, priority) { - // Update the priority of the given element (equivalent to decreaseKey) - const currPos = this.keys[key] - this._heap[currPos][1] = priority - const parentPos = getParentPosition(currPos) - const currPriority = this._heap[currPos][1] - let parentPriority = Infinity - if (parentPos >= 0) { - parentPriority = this._heap[parentPos][1] - } - const [child1Pos, child2Pos] = getChildrenPosition(currPos) - let [child1Priority, child2Priority] = [Infinity, Infinity] - if (child1Pos < this._heap.length) { - child1Priority = this._heap[child1Pos][1] - } - if (child2Pos < this._heap.length) { - child2Priority = this._heap[child2Pos][1] - } - - if (parentPos >= 0 && parentPriority > currPriority) { - this._shiftUp(currPos) - } else if (child2Pos < this._heap.length && - (child1Priority < currPriority || child2Priority < currPriority)) { - this._shiftDown(currPos) - } - } - - _shiftUp (position) { - // Helper function to shift up a node to proper position (equivalent to bubbleUp) - let currPos = position - let parentPos = getParentPosition(currPos) - let currPriority = this._heap[currPos][1] - let parentPriority = Infinity - if (parentPos >= 0) { - parentPriority = this._heap[parentPos][1] - } - - while (parentPos >= 0 && parentPriority > currPriority) { - this._swap(currPos, parentPos) - currPos = parentPos - parentPos = getParentPosition(currPos) - currPriority = this._heap[currPos][1] - try { - parentPriority = this._heap[parentPos][1] - } catch (error) { - parentPriority = Infinity - } - } - this.keys[this._heap[currPos][0]] = currPos - } - - _shiftDown (position) { - // Helper function to shift down a node to proper position (equivalent to bubbleDown) - let currPos = position - let [child1Pos, child2Pos] = getChildrenPosition(currPos) - let [child1Priority, child2Priority] = [Infinity, Infinity] - if (child1Pos < this._heap.length) { - child1Priority = this._heap[child1Pos][1] - } - if (child2Pos < this._heap.length) { - child2Priority = this._heap[child2Pos][1] - } - let currPriority - try { - currPriority = this._heap[currPos][1] - } catch { - return - } - - while (child2Pos < this._heap.length && - (child1Priority < currPriority || child2Priority < currPriority)) { - if (child1Priority < currPriority && child1Priority < child2Priority) { - this._swap(child1Pos, currPos) - currPos = child1Pos - } else { - this._swap(child2Pos, currPos) - currPos = child2Pos - } - [child1Pos, child2Pos] = getChildrenPosition(currPos) - try { - [child1Priority, child2Priority] = [this._heap[child1Pos][1], this._heap[child2Pos][1]] - } catch (error) { - [child1Priority, child2Priority] = [Infinity, Infinity] - } - - currPriority = this._heap[currPos][1] - } - this.keys[this._heap[currPos][0]] = currPos - if (child1Pos < this._heap.length && child1Priority < currPriority) { - this._swap(child1Pos, currPos) - this.keys[this._heap[child1Pos][0]] = child1Pos - } - } - - _swap (position1, position2) { - // Helper function to swap 2 nodes - [this._heap[position1], this._heap[position2]] = [this._heap[position2], this._heap[position1]] - this.keys[this._heap[position1][0]] = position1 - this.keys[this._heap[position2][0]] = position2 - } -} - +import { KeyPriorityQueue } from "../Data-Structures/Heap/KeyPriorityQueue" class GraphWeightedUndirectedAdjacencyList { // Weighted Undirected Graph class constructor () { @@ -167,7 +23,7 @@ class GraphWeightedUndirectedAdjacencyList { // Details: https://en.wikipedia.org/wiki/Prim%27s_algorithm const distance = {} const parent = {} - const priorityQueue = new PriorityQueue() + const priorityQueue = new KeyPriorityQueue() // Initialization for (const node in this.connections) { distance[node] = (node === start.toString() ? 0 : Infinity) @@ -198,11 +54,3 @@ class GraphWeightedUndirectedAdjacencyList { } export { GraphWeightedUndirectedAdjacencyList } - -// const graph = new GraphWeightedUndirectedAdjacencyList() -// graph.addEdge(1, 2, 1) -// graph.addEdge(2, 3, 2) -// graph.addEdge(3, 4, 1) -// graph.addEdge(3, 5, 100) // Removed in MST -// graph.addEdge(4, 5, 5) -// graph.PrimMST(1) From 3f3eacd4834bedab1b3f52c72746f3ba42f2805c Mon Sep 17 00:00:00 2001 From: Pauline Garelli Date: Wed, 22 Feb 2023 10:49:09 +0100 Subject: [PATCH 5/8] feat: add test for PrimMST #1298 --- Graphs/test/PrimMST.test.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Graphs/test/PrimMST.test.js diff --git a/Graphs/test/PrimMST.test.js b/Graphs/test/PrimMST.test.js new file mode 100644 index 0000000000..ca43a6f4e7 --- /dev/null +++ b/Graphs/test/PrimMST.test.js @@ -0,0 +1,20 @@ +import { GraphWeightedUndirectedAdjacencyList } from '../PrimMST.js' + +test('Test Case PrimMST 1', () => { + // create graph to compute MST on + const graph = new GraphWeightedUndirectedAdjacencyList() + graph.addEdge(1, 2, 1) + graph.addEdge(2, 3, 2) + graph.addEdge(3, 4, 1) + graph.addEdge(3, 5, 100) // Removed in MST + graph.addEdge(4, 5, 5) + // create expected graph + const expectedGraph = new GraphWeightedUndirectedAdjacencyList() + expectedGraph.addEdge(1, 2, 1) + expectedGraph.addEdge(2, 3, 2) + expectedGraph.addEdge(3, 4, 1) + expectedGraph.addEdge(4, 5, 5) + // result from MST + const res = graph.PrimMST(1) + expect(res).toEqual(expectedGraph) +}) \ No newline at end of file From 16fa8a135b474cf179a60aef15120af8724e7cbf Mon Sep 17 00:00:00 2001 From: Pauline Garelli Date: Wed, 22 Feb 2023 11:11:08 +0100 Subject: [PATCH 6/8] fix: format files #1298 --- Data-Structures/Heap/KeyPriorityQueue.js | 56 +++++++++---------- .../Heap/test/KeyPriorityQueue.test.js | 18 +++--- Graphs/PrimMST.js | 2 +- Graphs/test/PrimMST.test.js | 34 +++++------ 4 files changed, 50 insertions(+), 60 deletions(-) diff --git a/Data-Structures/Heap/KeyPriorityQueue.js b/Data-Structures/Heap/KeyPriorityQueue.js index 674b74a7cf..1307740243 100644 --- a/Data-Structures/Heap/KeyPriorityQueue.js +++ b/Data-Structures/Heap/KeyPriorityQueue.js @@ -1,15 +1,15 @@ /** * KeyPriorityQueue is a priority queue based on a Minimum Binary Heap. - * - * Minimum Binary Heaps are binary trees which are filled level by level + * + * Minimum Binary Heaps are binary trees which are filled level by level * and then from left to right inside a depth level. - * Their main property is that any parent node has a smaller or equal priority to all of its children, + * Their main property is that any parent node has a smaller or equal priority to all of its children, * hence the root of the tree always has the smallest priority of all nodes. - * + * * This implementation of the Minimum Binary Heap allows for nodes to contain both a key and a priority. * WARNING: keys must be Integers as they are used as array indices. - * - * In this implementation, the heap is represented by an array with nodes ordered + * + * In this implementation, the heap is represented by an array with nodes ordered * from root-to-leaf, left-to-right. * Therefore, the parent-child node relationship is such that * * the children nodes positions relative to their parent are: (parentPos * 2 + 1) and (parentPos * 2 + 2) @@ -18,38 +18,37 @@ * More information and visuals on Binary Heaps can be found here: https://www.geeksforgeeks.org/binary-heap/ */ - // Priority Queue Helper functions -function getParentPosition(position) { +function getParentPosition (position) { // Get the parent node of the current node return Math.floor((position - 1) / 2) } -function getChildrenPosition(position) { +function getChildrenPosition (position) { // Get the children nodes of the current node return [2 * position + 1, 2 * position + 2] } class KeyPriorityQueue { // Priority Queue class using Minimum Binary Heap - constructor() { + constructor () { this._heap = [] this.keys = {} } /** * Checks if the heap is empty - * @returns boolean + * @returns boolean */ - isEmpty() { + isEmpty () { return this._heap.length === 0 } /** * Adds an element to the queue - * @param {number} key - * @param {number} priority + * @param {number} key + * @param {number} priority */ - push(key, priority) { + push (key, priority) { this._heap.push([key, priority]) this.keys[key] = this._heap.length - 1 this._shiftUp(this.keys[key]) @@ -59,7 +58,7 @@ class KeyPriorityQueue { * Removes the element with least priority * @returns the key of the element with least priority */ - pop() { + pop () { this._swap(0, this._heap.length - 1) const [key] = this._heap.pop() delete this.keys[key] @@ -69,10 +68,10 @@ class KeyPriorityQueue { /** * Checks whether a given key is present in the queue - * @param {number} key + * @param {number} key * @returns boolean */ - contains(key) { + contains (key) { return (key in this.keys) } @@ -81,7 +80,7 @@ class KeyPriorityQueue { * @param {number} key the element to change * @param {number} priority new priority of the element */ - update(key, priority) { + update (key, priority) { const currPos = this.keys[key] this._heap[currPos][1] = priority const parentPos = getParentPosition(currPos) @@ -100,15 +99,11 @@ class KeyPriorityQueue { _getPriorityOrInfinite (position) { // Helper function, returns priority of the node, or Infinite if no node corresponds to this position - if (position >= 0 && position < this._heap.length) { - return this._heap[position][1] - } - else { - return Infinity - } + if (position >= 0 && position < this._heap.length) return this._heap[position][1] + else return Infinity } - _shiftUp(position) { + _shiftUp (position) { // Helper function to shift up a node to proper position (equivalent to bubbleUp) let currPos = position let parentPos = getParentPosition(currPos) @@ -121,12 +116,11 @@ class KeyPriorityQueue { parentPos = getParentPosition(currPos) currPriority = this._getPriorityOrInfinite(currPos) parentPriority = this._getPriorityOrInfinite(parentPos) - } this.keys[this._heap[currPos][0]] = currPos } - _shiftDown(position) { + _shiftDown (position) { // Helper function to shift down a node to proper position (equivalent to bubbleDown) let currPos = position let [child1Pos, child2Pos] = getChildrenPosition(currPos) @@ -134,7 +128,7 @@ class KeyPriorityQueue { let child2Priority = this._getPriorityOrInfinite(child2Pos) let currPriority = this._getPriorityOrInfinite(currPos) - if (currPriority == Infinity) { + if (currPriority === Infinity) { return } @@ -153,7 +147,7 @@ class KeyPriorityQueue { } } - _swap(position1, position2) { + _swap (position1, position2) { // Helper function to swap 2 nodes [this._heap[position1], this._heap[position2]] = [this._heap[position2], this._heap[position1]] this.keys[this._heap[position1][0]] = position1 @@ -161,4 +155,4 @@ class KeyPriorityQueue { } } -export { KeyPriorityQueue } \ No newline at end of file +export { KeyPriorityQueue } diff --git a/Data-Structures/Heap/test/KeyPriorityQueue.test.js b/Data-Structures/Heap/test/KeyPriorityQueue.test.js index 5a726e6cea..f946dff122 100644 --- a/Data-Structures/Heap/test/KeyPriorityQueue.test.js +++ b/Data-Structures/Heap/test/KeyPriorityQueue.test.js @@ -1,23 +1,21 @@ import { KeyPriorityQueue } from '../KeyPriorityQueue.js' describe('Method isEmpty', () => { - test('Check heap is empty', () => { const queue = new KeyPriorityQueue() - let res = queue.isEmpty() + const res = queue.isEmpty() expect(res).toEqual(true) }) test('Check heap is not empty', () => { const queue = new KeyPriorityQueue() queue.push(0, 2) - let res = queue.isEmpty() + const res = queue.isEmpty() expect(res).toEqual(false) }) }) describe('Methods push and pop', () => { - test('Test Case 1', () => { // create queue const queue = new KeyPriorityQueue() @@ -34,7 +32,7 @@ describe('Methods push and pop', () => { queue.pop() expect(queue).toEqual(expectedQueue) }) - + test('Test Case 2', () => { // create queue const queue = new KeyPriorityQueue() @@ -51,7 +49,7 @@ describe('Methods push and pop', () => { queue.pop() expect(queue).toEqual(expectedQueue) }) - + test('Test Case 3', () => { // create queue const queue = new KeyPriorityQueue() @@ -73,23 +71,21 @@ describe('Methods push and pop', () => { }) describe('Method contains', () => { - test('Check heap does not contain element', () => { const queue = new KeyPriorityQueue() - let res = queue.contains(0) + const res = queue.contains(0) expect(res).toEqual(false) }) test('Check heap contains element', () => { const queue = new KeyPriorityQueue() queue.push(0, 2) - let res = queue.contains(0) + const res = queue.contains(0) expect(res).toEqual(true) }) }) describe('Method update', () => { - test('Update without change in position', () => { // create queue const queue = new KeyPriorityQueue() @@ -125,4 +121,4 @@ describe('Method update', () => { queue.update(0, 9) expect(queue).toEqual(expectedQueue) }) -}) \ No newline at end of file +}) diff --git a/Graphs/PrimMST.js b/Graphs/PrimMST.js index c02c115fae..c658845874 100644 --- a/Graphs/PrimMST.js +++ b/Graphs/PrimMST.js @@ -1,4 +1,4 @@ -import { KeyPriorityQueue } from "../Data-Structures/Heap/KeyPriorityQueue" +import { KeyPriorityQueue } from '../Data-Structures/Heap/KeyPriorityQueue' class GraphWeightedUndirectedAdjacencyList { // Weighted Undirected Graph class constructor () { diff --git a/Graphs/test/PrimMST.test.js b/Graphs/test/PrimMST.test.js index ca43a6f4e7..82f3c033f1 100644 --- a/Graphs/test/PrimMST.test.js +++ b/Graphs/test/PrimMST.test.js @@ -1,20 +1,20 @@ import { GraphWeightedUndirectedAdjacencyList } from '../PrimMST.js' test('Test Case PrimMST 1', () => { - // create graph to compute MST on - const graph = new GraphWeightedUndirectedAdjacencyList() - graph.addEdge(1, 2, 1) - graph.addEdge(2, 3, 2) - graph.addEdge(3, 4, 1) - graph.addEdge(3, 5, 100) // Removed in MST - graph.addEdge(4, 5, 5) - // create expected graph - const expectedGraph = new GraphWeightedUndirectedAdjacencyList() - expectedGraph.addEdge(1, 2, 1) - expectedGraph.addEdge(2, 3, 2) - expectedGraph.addEdge(3, 4, 1) - expectedGraph.addEdge(4, 5, 5) - // result from MST - const res = graph.PrimMST(1) - expect(res).toEqual(expectedGraph) -}) \ No newline at end of file + // create graph to compute MST on + const graph = new GraphWeightedUndirectedAdjacencyList() + graph.addEdge(1, 2, 1) + graph.addEdge(2, 3, 2) + graph.addEdge(3, 4, 1) + graph.addEdge(3, 5, 100) // Removed in MST + graph.addEdge(4, 5, 5) + // create expected graph + const expectedGraph = new GraphWeightedUndirectedAdjacencyList() + expectedGraph.addEdge(1, 2, 1) + expectedGraph.addEdge(2, 3, 2) + expectedGraph.addEdge(3, 4, 1) + expectedGraph.addEdge(4, 5, 5) + // result from MST + const res = graph.PrimMST(1) + expect(res).toEqual(expectedGraph) +}) From 33a1f148f3cd089529bbb483c93c256e14095624 Mon Sep 17 00:00:00 2001 From: Pauline Garelli Date: Wed, 22 Feb 2023 22:53:42 +0100 Subject: [PATCH 7/8] fix: minor coding style changes --- Data-Structures/Heap/KeyPriorityQueue.js | 16 +- .../Heap/test/KeyPriorityQueue.test.js | 220 +++++++++--------- 2 files changed, 116 insertions(+), 120 deletions(-) diff --git a/Data-Structures/Heap/KeyPriorityQueue.js b/Data-Structures/Heap/KeyPriorityQueue.js index 1307740243..1441a026b9 100644 --- a/Data-Structures/Heap/KeyPriorityQueue.js +++ b/Data-Structures/Heap/KeyPriorityQueue.js @@ -19,14 +19,8 @@ */ // Priority Queue Helper functions -function getParentPosition (position) { - // Get the parent node of the current node - return Math.floor((position - 1) / 2) -} -function getChildrenPosition (position) { - // Get the children nodes of the current node - return [2 * position + 1, 2 * position + 2] -} +const getParentPosition = position => Math.floor((position - 1) / 2) +const getChildrenPositions = position => [2 * position + 1, 2 * position + 2] class KeyPriorityQueue { // Priority Queue class using Minimum Binary Heap @@ -86,7 +80,7 @@ class KeyPriorityQueue { const parentPos = getParentPosition(currPos) const currPriority = this._getPriorityOrInfinite(currPos) const parentPriority = this._getPriorityOrInfinite(parentPos) - const [child1Pos, child2Pos] = getChildrenPosition(currPos) + const [child1Pos, child2Pos] = getChildrenPositions(currPos) const child1Priority = this._getPriorityOrInfinite(child1Pos) const child2Priority = this._getPriorityOrInfinite(child2Pos) @@ -123,7 +117,7 @@ class KeyPriorityQueue { _shiftDown (position) { // Helper function to shift down a node to proper position (equivalent to bubbleDown) let currPos = position - let [child1Pos, child2Pos] = getChildrenPosition(currPos) + let [child1Pos, child2Pos] = getChildrenPositions(currPos) let child1Priority = this._getPriorityOrInfinite(child1Pos) let child2Priority = this._getPriorityOrInfinite(child2Pos) let currPriority = this._getPriorityOrInfinite(currPos) @@ -140,7 +134,7 @@ class KeyPriorityQueue { this._swap(child2Pos, currPos) currPos = child2Pos } - [child1Pos, child2Pos] = getChildrenPosition(currPos) + [child1Pos, child2Pos] = getChildrenPositions(currPos) child1Priority = this._getPriorityOrInfinite(child1Pos) child2Priority = this._getPriorityOrInfinite(child2Pos) currPriority = this._getPriorityOrInfinite(currPos) diff --git a/Data-Structures/Heap/test/KeyPriorityQueue.test.js b/Data-Structures/Heap/test/KeyPriorityQueue.test.js index f946dff122..b9e9ea6202 100644 --- a/Data-Structures/Heap/test/KeyPriorityQueue.test.js +++ b/Data-Structures/Heap/test/KeyPriorityQueue.test.js @@ -1,124 +1,126 @@ import { KeyPriorityQueue } from '../KeyPriorityQueue.js' -describe('Method isEmpty', () => { - test('Check heap is empty', () => { - const queue = new KeyPriorityQueue() - const res = queue.isEmpty() - expect(res).toEqual(true) - }) +describe('Key Priority Queue', () => { + describe('Method isEmpty', () => { + test('Check heap is empty', () => { + const queue = new KeyPriorityQueue() + const res = queue.isEmpty() + expect(res).toEqual(true) + }) - test('Check heap is not empty', () => { - const queue = new KeyPriorityQueue() - queue.push(0, 2) - const res = queue.isEmpty() - expect(res).toEqual(false) + test('Check heap is not empty', () => { + const queue = new KeyPriorityQueue() + queue.push(0, 2) + const res = queue.isEmpty() + expect(res).toEqual(false) + }) }) -}) -describe('Methods push and pop', () => { - test('Test Case 1', () => { - // create queue - const queue = new KeyPriorityQueue() - queue.push(0, 3) - queue.push(1, 7) - queue.push(2, 9) - queue.push(3, 13) - // create expected queue - const expectedQueue = new KeyPriorityQueue() - expectedQueue.push(1, 7) - expectedQueue.push(3, 13) - expectedQueue.push(2, 9) - // result from popping element from the queue - queue.pop() - expect(queue).toEqual(expectedQueue) - }) + describe('Methods push and pop', () => { + test('Test Case 1', () => { + // create queue + const queue = new KeyPriorityQueue() + queue.push(0, 3) + queue.push(1, 7) + queue.push(2, 9) + queue.push(3, 13) + // create expected queue + const expectedQueue = new KeyPriorityQueue() + expectedQueue.push(1, 7) + expectedQueue.push(3, 13) + expectedQueue.push(2, 9) + // result from popping element from the queue + queue.pop() + expect(queue).toEqual(expectedQueue) + }) - test('Test Case 2', () => { - // create queue - const queue = new KeyPriorityQueue() - queue.push(0, 3) - queue.push(1, 9) - queue.push(2, 7) - queue.push(3, 13) - // create expected queue - const expectedQueue = new KeyPriorityQueue() - expectedQueue.push(2, 7) - expectedQueue.push(1, 9) - expectedQueue.push(3, 13) - // result from popping element from the queue - queue.pop() - expect(queue).toEqual(expectedQueue) - }) + test('Test Case 2', () => { + // create queue + const queue = new KeyPriorityQueue() + queue.push(0, 3) + queue.push(1, 9) + queue.push(2, 7) + queue.push(3, 13) + // create expected queue + const expectedQueue = new KeyPriorityQueue() + expectedQueue.push(2, 7) + expectedQueue.push(1, 9) + expectedQueue.push(3, 13) + // result from popping element from the queue + queue.pop() + expect(queue).toEqual(expectedQueue) + }) - test('Test Case 3', () => { - // create queue - const queue = new KeyPriorityQueue() - queue.push(0, 3) - queue.push(1, 7) - queue.push(2, 9) - queue.push(3, 12) - queue.push(4, 13) - // create expected queue - const expectedQueue = new KeyPriorityQueue() - expectedQueue.push(1, 7) - expectedQueue.push(3, 12) - expectedQueue.push(2, 9) - expectedQueue.push(4, 13) - // result from popping element from the queue - queue.pop() - expect(queue).toEqual(expectedQueue) + test('Test Case 3', () => { + // create queue + const queue = new KeyPriorityQueue() + queue.push(0, 3) + queue.push(1, 7) + queue.push(2, 9) + queue.push(3, 12) + queue.push(4, 13) + // create expected queue + const expectedQueue = new KeyPriorityQueue() + expectedQueue.push(1, 7) + expectedQueue.push(3, 12) + expectedQueue.push(2, 9) + expectedQueue.push(4, 13) + // result from popping element from the queue + queue.pop() + expect(queue).toEqual(expectedQueue) + }) }) -}) -describe('Method contains', () => { - test('Check heap does not contain element', () => { - const queue = new KeyPriorityQueue() - const res = queue.contains(0) - expect(res).toEqual(false) - }) + describe('Method contains', () => { + test('Check heap does not contain element', () => { + const queue = new KeyPriorityQueue() + const res = queue.contains(0) + expect(res).toEqual(false) + }) - test('Check heap contains element', () => { - const queue = new KeyPriorityQueue() - queue.push(0, 2) - const res = queue.contains(0) - expect(res).toEqual(true) + test('Check heap contains element', () => { + const queue = new KeyPriorityQueue() + queue.push(0, 2) + const res = queue.contains(0) + expect(res).toEqual(true) + }) }) -}) -describe('Method update', () => { - test('Update without change in position', () => { - // create queue - const queue = new KeyPriorityQueue() - queue.push(0, 3) - queue.push(1, 5) - queue.push(2, 7) - queue.push(3, 11) - // create expected queue - const expectedQueue = new KeyPriorityQueue() - expectedQueue.push(0, 2) - expectedQueue.push(1, 5) - expectedQueue.push(2, 7) - expectedQueue.push(3, 11) - // result from updating to similar priority - queue.update(0, 2) - expect(queue).toEqual(expectedQueue) - }) + describe('Method update', () => { + test('Update without change in position', () => { + // create queue + const queue = new KeyPriorityQueue() + queue.push(0, 3) + queue.push(1, 5) + queue.push(2, 7) + queue.push(3, 11) + // create expected queue + const expectedQueue = new KeyPriorityQueue() + expectedQueue.push(0, 2) + expectedQueue.push(1, 5) + expectedQueue.push(2, 7) + expectedQueue.push(3, 11) + // result from updating to similar priority + queue.update(0, 2) + expect(queue).toEqual(expectedQueue) + }) - test('Update with change in position', () => { - // create queue - const queue = new KeyPriorityQueue() - queue.push(0, 3) - queue.push(1, 5) - queue.push(2, 7) - queue.push(3, 11) - // create expected queue - const expectedQueue = new KeyPriorityQueue() - expectedQueue.push(1, 5) - expectedQueue.push(3, 11) - expectedQueue.push(2, 7) - expectedQueue.push(0, 9) - // result from updating to similar priority - queue.update(0, 9) - expect(queue).toEqual(expectedQueue) + test('Update with change in position', () => { + // create queue + const queue = new KeyPriorityQueue() + queue.push(0, 3) + queue.push(1, 5) + queue.push(2, 7) + queue.push(3, 11) + // create expected queue + const expectedQueue = new KeyPriorityQueue() + expectedQueue.push(1, 5) + expectedQueue.push(3, 11) + expectedQueue.push(2, 7) + expectedQueue.push(0, 9) + // result from updating to similar priority + queue.update(0, 9) + expect(queue).toEqual(expectedQueue) + }) }) }) From 8543f0555b9b711da1b4f8db46ee3266b5a327f0 Mon Sep 17 00:00:00 2001 From: Pauline Garelli Date: Thu, 23 Feb 2023 00:04:39 +0100 Subject: [PATCH 8/8] fix: use map for keys and priorities #1298 --- Data-Structures/Heap/KeyPriorityQueue.js | 41 ++++++++++++------------ 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/Data-Structures/Heap/KeyPriorityQueue.js b/Data-Structures/Heap/KeyPriorityQueue.js index 1441a026b9..e7bbede45e 100644 --- a/Data-Structures/Heap/KeyPriorityQueue.js +++ b/Data-Structures/Heap/KeyPriorityQueue.js @@ -6,10 +6,10 @@ * Their main property is that any parent node has a smaller or equal priority to all of its children, * hence the root of the tree always has the smallest priority of all nodes. * - * This implementation of the Minimum Binary Heap allows for nodes to contain both a key and a priority. - * WARNING: keys must be Integers as they are used as array indices. + * This implementation of the Minimum Binary Heap allows for nodes to be associated to both a key, + * which can be any datatype, and a priority. * - * In this implementation, the heap is represented by an array with nodes ordered + * The heap is represented by an array with nodes ordered * from root-to-leaf, left-to-right. * Therefore, the parent-child node relationship is such that * * the children nodes positions relative to their parent are: (parentPos * 2 + 1) and (parentPos * 2 + 2) @@ -26,7 +26,7 @@ class KeyPriorityQueue { // Priority Queue class using Minimum Binary Heap constructor () { this._heap = [] - this.keys = {} + this.priorities = new Map() } /** @@ -39,13 +39,13 @@ class KeyPriorityQueue { /** * Adds an element to the queue - * @param {number} key + * @param {*} key * @param {number} priority */ push (key, priority) { - this._heap.push([key, priority]) - this.keys[key] = this._heap.length - 1 - this._shiftUp(this.keys[key]) + this._heap.push(key) + this.priorities.set(key, priority) + this._shiftUp(this._heap.length - 1) } /** @@ -54,29 +54,33 @@ class KeyPriorityQueue { */ pop () { this._swap(0, this._heap.length - 1) - const [key] = this._heap.pop() - delete this.keys[key] + const key = this._heap.pop() + this.priorities.delete(key) this._shiftDown(0) return key } /** * Checks whether a given key is present in the queue - * @param {number} key + * @param {*} key * @returns boolean */ contains (key) { - return (key in this.keys) + return this.priorities.has(key) } /** - * Updates the priority of the given element - * @param {number} key the element to change + * Updates the priority of the given element. + * Adds the element if it is not in the queue. + * @param {*} key the element to change * @param {number} priority new priority of the element */ update (key, priority) { - const currPos = this.keys[key] - this._heap[currPos][1] = priority + const currPos = this._heap.indexOf(key) + // if the key does not exist yet, add it + if (currPos === -1) return this.push(key, priority) + // else update priority + this.priorities.set(key, priority) const parentPos = getParentPosition(currPos) const currPriority = this._getPriorityOrInfinite(currPos) const parentPriority = this._getPriorityOrInfinite(parentPos) @@ -93,7 +97,7 @@ class KeyPriorityQueue { _getPriorityOrInfinite (position) { // Helper function, returns priority of the node, or Infinite if no node corresponds to this position - if (position >= 0 && position < this._heap.length) return this._heap[position][1] + if (position >= 0 && position < this._heap.length) return this.priorities.get(this._heap[position]) else return Infinity } @@ -111,7 +115,6 @@ class KeyPriorityQueue { currPriority = this._getPriorityOrInfinite(currPos) parentPriority = this._getPriorityOrInfinite(parentPos) } - this.keys[this._heap[currPos][0]] = currPos } _shiftDown (position) { @@ -144,8 +147,6 @@ class KeyPriorityQueue { _swap (position1, position2) { // Helper function to swap 2 nodes [this._heap[position1], this._heap[position2]] = [this._heap[position2], this._heap[position1]] - this.keys[this._heap[position1][0]] = position1 - this.keys[this._heap[position2][0]] = position2 } }