Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved the Heap.js File #222

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
201 changes: 110 additions & 91 deletions src/data-structures/heap.js
Original file line number Diff line number Diff line change
@@ -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 }
Expand All @@ -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);
Expand All @@ -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);