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 } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy