From 9c6e44e5ec1a3ac4177b65990b369d41e6873bb0 Mon Sep 17 00:00:00 2001 From: Abdullah Nazir <abdullah03096344305@gmail.com> Date: Wed, 25 Dec 2024 14:31:31 +0500 Subject: [PATCH] Improved the Heap.js File by adding more comments and increase deep understanding. --- src/data-structures/heap.js | 201 ++++++++++++++++++++---------------- 1 file changed, 110 insertions(+), 91 deletions(-) diff --git a/src/data-structures/heap.js b/src/data-structures/heap.js index cb936575..e329f831 100644 --- a/src/data-structures/heap.js +++ b/src/data-structures/heap.js @@ -1,34 +1,22 @@ /** - * A binary heap is a complete binary tree which - * satisfies the heap ordering property. + * A binary heap is a complete binary tree that satisfies the heap ordering property. + * This implementation supports both minimum and maximum heaps, depending on the + * comparison function provided. If no comparison function is provided, it defaults to + * a minimum heap. * * @example * var Heap = require('path-to-algorithms/src/data-structures/heap').Heap; * + * // Example for a max heap using birthyear for comparison: * var heap = new Heap(function(a, b) { * return a.birthyear - b.birthyear; * }); * - * heap.add({ - * name: 'John', - * birthyear: 1981 - * }); - * heap.add({ - * name: 'Pavlo', - * birthyear: 2000 - * }); - * heap.add({ - * name: 'Garry', - * birthyear: 1989 - * }); - * heap.add({ - * name: 'Derek', - * birthyear: 1990 - * }); - * heap.add({ - * name: 'Ivan', - * birthyear: 1966 - * }); + * heap.add({ name: 'John', birthyear: 1981 }); + * heap.add({ name: 'Pavlo', birthyear: 2000 }); + * heap.add({ name: 'Garry', birthyear: 1989 }); + * heap.add({ name: 'Derek', birthyear: 1990 }); + * heap.add({ name: 'Ivan', birthyear: 1966 }); * * console.log(heap.extract()); // { name: 'Pavlo', birthyear: 2000 } * console.log(heap.extract()); // { name: 'Derek', birthyear: 1990 } @@ -39,96 +27,111 @@ * @module data-structures/heap */ (function (exports) { - - 'use strict'; + "use strict"; /** - * Minimum heap constructor. + * Heap constructor. + * The heap can be either a min-heap or max-heap, determined by the comparison function. + * By default, it behaves as a min-heap if no comparison function is provided. * * @public * @constructor - * @param {Function} cmp Function used for comparison between the elements. + * @param {Function} cmp A function used for comparing elements (optional). + * It should return a positive number if a > b, + * a negative number if a < b, and 0 if they are equal. */ exports.Heap = function (cmp) { this._heap = []; - if (typeof cmp === 'function') { - this._cmp = cmp; - } else { - this._cmp = function (a, b) { - return a - b; - }; - } + // If comparison function is provided, use it; otherwise, use default for min-heap. + this._cmp = + typeof cmp === "function" + ? cmp + : function (a, b) { + return a - b; + }; }; /** - * Exchange indexes with start index given as argument - * to turn the tree into a valid heap. On a single call - * this method maintains only a single "branch" of the heap.<br><br> + * Heapifies the subtree rooted at the given index, maintaining the heap property. + * This method assumes that the subtrees are already valid heaps and fixes the + * heap rooted at the provided index. * * Time complexity: O(log N). * * @private - * @param {Number} index The parent. + * @param {Number} index Index of the node to heapify. */ exports.Heap.prototype._heapify = function (index) { - var extr = index; - var left = 2 * index + 1; - var right = 2 * index + 2; + var largest = index; + var left = 2 * index + 1; // Left child index + var right = 2 * index + 2; // Right child index var temp; - if (left < this._heap.length && - this._cmp(this._heap[left], this._heap[index]) > 0) { - extr = left; + // Compare the left child with the current node (index). + if ( + left < this._heap.length && + this._cmp(this._heap[left], this._heap[index]) > 0 + ) { + largest = left; } - if (right < this._heap.length && - this._cmp(this._heap[right], this._heap[index]) > 0 && - this._cmp(this._heap[right], this._heap[left]) > 0) { - extr = right; + // Compare the right child with the current largest node. + if ( + right < this._heap.length && + this._cmp(this._heap[right], this._heap[largest]) > 0 + ) { + largest = right; } - if (index !== extr) { + // If the largest node is not the current index, swap and heapify. + if (largest !== index) { temp = this._heap[index]; - this._heap[index] = this._heap[extr]; - this._heap[extr] = temp; - this._heapify(extr); + this._heap[index] = this._heap[largest]; + this._heap[largest] = temp; + // Recursively heapify the affected subtree. + this._heapify(largest); } }; /** - * Changes the key.<br><br> - * Complexity: O(log N). + * Changes the key/value of the element at the given index and repositions it + * to maintain the heap property. + * + * Time complexity: O(log N). * * @public - * @param {Number} index Index of the value which should be changed. - * @param {Number|Object} value New value according to the index. - * @return {Number} New position of the element. + * @param {Number} index Index of the element to change. + * @param {Number|Object} value The new value for the element. + * @return {Number} The new position of the element. */ exports.Heap.prototype.changeKey = function (index, value) { this._heap[index] = value; var elem = this._heap[index]; - var parent = Math.floor(index / 2); + var parent = Math.floor((index - 1) / 2); // Parent index (use (index-1)/2 for zero-based index). var temp; - if (elem !== undefined) { - while (parent >= 0 && this._cmp(elem, this._heap[parent]) > 0) { - temp = this._heap[parent]; - this._heap[parent] = elem; - this._heap[index] = temp; - index = parent; - parent = Math.floor(parent / 2); - } - this._heapify(index); + + // Move the element up the tree if it's greater than its parent. + while (index > 0 && this._cmp(elem, this._heap[parent]) > 0) { + temp = this._heap[parent]; + this._heap[parent] = elem; + this._heap[index] = temp; + index = parent; + parent = Math.floor((parent - 1) / 2); } - return parent; + + // Ensure the heap is valid after key change. + this._heapify(index); + return index; }; /** - * Updates a given node. This operation is useful - * in algorithms like Dijkstra, A* where we need - * to decrease/increase value of the given node. + * Updates a specific node by repositioning it in the heap to maintain the heap property. + * This is useful in algorithms where node values need to be updated, such as Dijkstra's or A*. + * + * Time complexity: O(log N). * * @public - * @param {Number|Object} node Node which should be updated. + * @param {Number|Object} node The node to be updated. */ exports.Heap.prototype.update = function (node) { var idx = this._heap.indexOf(node); @@ -138,58 +141,74 @@ }; /** - * Adds new element to the heap.<br><br> - * Complexity: O(log N). + * Adds a new element to the heap, maintaining the heap property. + * + * Time complexity: O(log N). * * @public - * @param {Number|Object} value Value which will be inserted. - * @return {Number} Index of the inserted value. + * @param {Number|Object} value The value to be added to the heap. + * @return {Number} The index of the inserted value. */ exports.Heap.prototype.add = function (value) { + // Insert the value at the end of the array and then move it to the correct position. this._heap.push(value); return this.changeKey(this._heap.length - 1, value); }; /** - * Returns current value which is on the top of the heap.<br><br> - * Complexity: O(1). + * Retrieves, but does not remove, the element at the top of the heap (the extremum). + * + * Time complexity: O(1). * * @public - * @return {Number|Object} Current top value. + * @return {Number|Object} The current top value of the heap. */ exports.Heap.prototype.top = function () { return this._heap[0]; }; /** - * Removes and returns the current extremum value - * which is on the top of the heap.<br><br> - * Complexity: O(log N). + * Removes and returns the top (extremum) element from the heap. + * After removal, the heap property is restored. + * + * Time complexity: O(log N). * * @public - * @returns {Number|Object} The extremum value. + * @return {Number|Object} The extremum value removed from the heap. + * @throws Will throw an error if the heap is empty. */ exports.Heap.prototype.extract = function () { - if (!this._heap.length) { - throw 'The heap is already empty!'; + if (this._heap.length === 0) { + throw new Error("The heap is already empty!"); + } + + // Swap the top element with the last one, remove the last, and heapify the top. + var extr = this._heap[0]; + var last = this._heap.pop(); + if (this._heap.length > 0) { + this._heap[0] = last; + this._heapify(0); } - var extr = this._heap.shift(); - this._heapify(0); return extr; }; + /** + * Returns the entire collection of the heap's elements. + * + * @public + * @return {Array} An array representing the heap's internal collection. + */ exports.Heap.prototype.getCollection = function () { return this._heap; }; /** - * Checks or heap is empty. + * Checks if the heap is empty. * * @public - * @returns {Boolean} Returns true if heap is empty. + * @return {Boolean} True if the heap is empty, otherwise false. */ exports.Heap.prototype.isEmpty = function () { - return !this._heap.length; + return this._heap.length === 0; }; - -})(typeof window === 'undefined' ? module.exports : window); +})(typeof window === "undefined" ? module.exports : window);