From ffc397b5aa32d3cfc97396f5d28e3128ad2cbba4 Mon Sep 17 00:00:00 2001 From: Eyas Ranjous Date: Sun, 15 Dec 2019 17:59:15 -0600 Subject: [PATCH 1/8] initial files --- .eslintrc | 23 ++++ .gitignore | 3 + .npmignore | 5 + .travis.yml | 10 ++ Gruntfile.js | 27 +++++ LICENSE | 21 ++++ README.md | 109 +++++++++++++++++- index.js | 7 ++ package.json | 40 +++++++ src/heap.js | 255 +++++++++++++++++++++++++++++++++++++++++++ src/heapNode.js | 45 ++++++++ src/maxHeap.js | 104 ++++++++++++++++++ src/minHeap.js | 92 ++++++++++++++++ test/maxHeap.test.js | 139 +++++++++++++++++++++++ test/minHeap.test.js | 139 +++++++++++++++++++++++ 15 files changed, 1017 insertions(+), 2 deletions(-) create mode 100644 .eslintrc create mode 100644 .gitignore create mode 100644 .npmignore create mode 100644 .travis.yml create mode 100644 Gruntfile.js create mode 100644 LICENSE create mode 100644 index.js create mode 100644 package.json create mode 100644 src/heap.js create mode 100644 src/heapNode.js create mode 100644 src/maxHeap.js create mode 100644 src/minHeap.js create mode 100644 test/maxHeap.test.js create mode 100644 test/minHeap.test.js diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..90d60f0 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,23 @@ +{ + "rules": { + max-len: [ + "error", + { + "code": 80, + "ignoreComments": true + } + ], + "comma-dangle": [ + "error", + { + "functions": "ignore" + } + ], + "class-methods-use-this":0 + }, + "env": { + "mocha": true, + "node": true + }, + "extends": ["airbnb-base"] +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3ba3e2b --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules +coverage +.DS_Store diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..d74ae6a --- /dev/null +++ b/.npmignore @@ -0,0 +1,5 @@ +.git* +test +.travis.yml +Gruntfile.js +coverage/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..61dd491 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,10 @@ +language: node_js +node_js: + - "10" + - "11" + - "12" +install: + - npm install -g grunt-cli + - npm install +script: + - grunt build diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..bc33154 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,27 @@ +module.exports = (grunt) => { + grunt.initConfig({ + eslint: { + src: ['src/*.js', 'test/*.test.js'] + }, + mochaTest: { + files: ['test/*.test.js'] + }, + mocha_istanbul: { + coverage: { + src: 'test', + options: { + mask: '*.test.js' + } + } + } + }); + + grunt.loadNpmTasks('grunt-eslint'); + grunt.loadNpmTasks('grunt-mocha-test'); + grunt.loadNpmTasks('grunt-mocha-istanbul'); + + grunt.registerTask('lint', ['eslint']); + grunt.registerTask('test', ['mochaTest']); + grunt.registerTask('coverage', ['mocha_istanbul']); + grunt.registerTask('build', ['lint', 'coverage']); +}; diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..894fe4b --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019 Eyas Ranjous + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 6ff9326..3dcd697 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,107 @@ -# heap -Heap (min & max) implementation in javascript +# @datastructures-js/heap +Javscript implementation for the Min/Max Heap Data Structures & Heap Sort Algorithm. + +[![build:?](https://travis-ci.org/datastructures-js/heap.svg?branch=master)](https://travis-ci.org/datastructures-js/heap) +[![npm](https://img.shields.io/npm/v/@datastructures-js/heap.svg)](https://www.npmjs.com/package/@datastructures-js/heap) +[![npm](https://img.shields.io/npm/dm/@datastructures-js/heap.svg)](https://www.npmjs.com/package/@datastructures-js/heap) [![npm](https://img.shields.io/badge/node-%3E=%206.0-blue.svg)](https://www.npmjs.com/package/@datastructures-js/heap) + +# API + +## install +```sh +npm install --save @datastructures-js/heap +``` + +## require/import +### require +```js +const { MinHeap, MaxHeap } = require('@datastructures-js/heap'); +``` + +### import +```js +import { MinHeap, MaxHeap } from '@datastructures-js/heap'; +``` + +## Create a Heap + +### Create an empty instance +```js +const minHeap = new MinHeap(); + +const maxHeap = new MaxHeap(); +``` + +### .heapify(list) +A static function that converts an array of items into a heap structure. The list items can be **number**, **string**, or a serialized heap node like `{ key: 10, value: { someProp: 'someVal' } }`. + +```js +const list = [50, 30, 80, 1, 7, 8]; + +const minHeap = MinHeap.heapify(list); + +const maxHeap = MaxHeap.heapify(list); +``` + +## .insert(key, value) +insert a node into the heap. Heap nodes are instances of the class NodeHeap. +```js +minHeap.insert(50); +minHeap.insert(80); +minHeap.insert(30, 'something'); +minHeap.insert(90); +minHeap.insert(60, null); +minHeap.insert(40); +minHeap.insert(20, { name: 'test' }); +``` + +## .root() +returns (peeks) the root node without removing it. The returned node is an instance of **NodeHeap** class which implements the following interface: + +### .getKey() +returns the node's key (number or string) that is used to compare with other nodes key. + +### .getValue() +returns the value that is associated with the key. Value can be any object type. + +### .serialize() +returns an object literal of key/value of the node. + +```js +const min = minHeap.root(); + +console.log(min.getKey()); // 20 +console.log(min.getValue()); // { name: 'test' } +console.log(min.serialize()); // { key: 20, value: { name: 'test' } } +``` + +## .extractRoot() +returns and remove the root node in the heap. + +```js +const min = minHeap.extractRoot(); + +console.log(min.getKey()); // 20 +console.log(min.getValue()); // { name: 'test' } +console.log(min.serialize()); // { key: 20, value: { name: 'test' } } + +console.log(minHeap.root().getKey()); // 30 +``` + +## .size() +returns the number of nodes in the heap +```js +console.log(minHeap.size()); // 6 +``` + +## .sort() +Sorting is implemented for both Max Heap (sorting keys in ascending order) & Min Heap (sorting keys in descending order). Calling sort on the heap directly mutates its nodes order. To avoid that and sort a heap while maintaining the structure, you can sort a clone. + +```js +const sortedDesc = minHeap.clone().sort(); +const sortedAsc = maxHeap.clone().sort(); +``` +## .clear() + +## .clone() +Creates a shallow clone of a heap by slicing the nodes array and passing it to a new heap instance. diff --git a/index.js b/index.js new file mode 100644 index 0000000..c5c6a14 --- /dev/null +++ b/index.js @@ -0,0 +1,7 @@ +const MinHeap = require('./src/minHeap'); +const MaxHeap = require('./src/maxHeap'); + +module.exports = { + MinHeap, + MaxHeap +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..22ae806 --- /dev/null +++ b/package.json @@ -0,0 +1,40 @@ +{ + "name": "@datastructures-js/heap", + "version": "0.0.0", + "description": "Heap (min & max) implementation in javascript", + "main": "index.js", + "scripts": { + "test": "grunt test" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/datastructures-js/heap.git" + }, + "keywords": [ + "heap", + "min heap", + "max heap", + "heap js", + "heap es6", + "heap sort", + "heapify" + ], + "author": "Eyas Ranjous ", + "license": "MIT", + "bugs": { + "url": "https://github.com/datastructures-js/heap/issues" + }, + "homepage": "https://github.com/datastructures-js/heap#readme", + "devDependencies": { + "chai": "^4.2.0", + "eslint": "^6.7.2", + "eslint-config-airbnb-base": "^14.0.0", + "eslint-plugin-import": "^2.19.1", + "grunt": "^1.0.4", + "grunt-eslint": "^22.0.0", + "grunt-mocha-istanbul": "^5.0.2", + "grunt-mocha-test": "^0.13.3", + "istanbul": "^0.4.5", + "mocha": "^6.2.2" + } +} diff --git a/src/heap.js b/src/heap.js new file mode 100644 index 0000000..75135b7 --- /dev/null +++ b/src/heap.js @@ -0,0 +1,255 @@ +/** + * datastructures-js/heap + * @copyright 2019 Eyas Ranjous + * @license MIT + */ + +const HeapNode = require('./heapNode'); + +const isNumber = (n) => typeof n === 'number'; +const isNoneEmptyString = (s) => typeof s === 'string' && s.length; +const isNoneNullObject = (o) => typeof o === 'object' && o !== null; +const isNoneEmptyArray = (a) => Array.isArray(a) && a.length > 0; + +/** + * @class Heap + * @abstract + */ +class Heap { + constructor() { + this.init(); + } + + /** + * initializes the heap nodes list + * @internal + */ + init(nodes) { + this.nodes = nodes || []; + } + + /** + * calculates the left child's index of a parent's index + * @private + * @param {number} parentIndex + * @returns {number} + */ + getLeftChildIndex(parentIndex) { + return (parentIndex * 2) + 1; + } + + /** + * calculates the right child's index of a parent's index + * @private + * @param {number} parentIndex + * @returns {number} + */ + getRightChildIndex(parentIndex) { + return (parentIndex * 2) + 2; + } + + /** + * calculates a parent's index from a child's index + * @private + * @param {number} parentIndex + * @returns {number} + */ + getParentIndex(childIndex) { + return Math.floor((childIndex - 1) / 2); + } + + /** + * gets the last node's index + * @private + * @returns {number} + */ + getLastIndex() { + return this.nodes.length - 1; + } + + /** + * swaps two nodes in the heap by their indices + * @param {number} i + * @param {number} j + */ + swap(i, j) { + const temp = this.nodes[i]; + this.nodes[i] = this.nodes[j]; + this.nodes[j] = temp; + } + + /** + * returns the correct child's index to fix the heap + * @private + * @param {number} parentIndex + * @returns {number} + */ + compareChildrenOf(parentIndex) { + const leftChildIndex = this.getLeftChildIndex(parentIndex); + const rightChildIndex = this.getRightChildIndex(parentIndex); + const size = this.size(); + + if (leftChildIndex >= size && rightChildIndex >= size) return 0; + if (leftChildIndex >= size) return rightChildIndex; + if (rightChildIndex >= size) return leftChildIndex; + + return this.compareChildren(leftChildIndex, rightChildIndex); + } + + /** + * bubbles the last inserted node up in the heap + * @private + */ + heapifyUp() { + let childIndex = this.getLastIndex(); + let parentIndex = this.getParentIndex(childIndex); + while (childIndex > 0 && this.shouldSwap(childIndex, parentIndex)) { + this.swap(childIndex, parentIndex); + childIndex = parentIndex; + parentIndex = this.getParentIndex(childIndex); + } + } + + /** + * pushes the replaced root node down in the heap after root's removal + * @private + */ + heapifyDown() { + let parentIndex = 0; + let childIndex = this.compareChildrenOf(parentIndex); + while (childIndex > 0 && this.shouldSwap(childIndex, parentIndex)) { + this.swap(childIndex, parentIndex); + parentIndex = childIndex; + childIndex = this.compareChildrenOf(parentIndex); + } + } + + /** + * pushes the swapped node with root down in its correct location + * @param {number} i - swapped node's index + * @private + */ + heapifyDownUntil(i) { + let parentIndex = 0; + let leftChildIndex = 1; + let rightChildIndex = 2; + let childIndex; + while (rightChildIndex < i) { + childIndex = this.compareChildrenUntil( + i, leftChildIndex, rightChildIndex + ); + + if (this.shouldSwap(childIndex, parentIndex)) { + this.swap(childIndex, parentIndex); + } + + parentIndex = childIndex; + leftChildIndex = this.getLeftChildIndex(parentIndex); + rightChildIndex = this.getRightChildIndex(parentIndex); + } + } + + /** + * implements heap sort algorithm by swapping root with i nodes + * @public + * @returns {array} sorted nodes list + */ + sort() { + for (let i = this.getLastIndex(); i > 0; i -= 1) { + this.swap(0, i); + this.heapifyDownUntil(i); + } + return this.nodes; + } + + /** + * applies a shallow clone on a heap + * @protected + * @returns {Heap} + */ + clone(HeapType) { + const heap = new HeapType(); + heap.init(this.nodes.slice()); + return heap; + } + + /** + * inserts a node into the heap + * @param {number|string} key + * @param {object} value + * @public + */ + insert(key, value) { + this.nodes.push(new HeapNode(key, value)); + this.heapifyUp(); + } + + /** + * removes and returns the root node in the heap + * @public + * @returns {HeapNode} + */ + extractRoot() { + if (this.size() === 0) return null; + + const root = this.nodes[0]; + this.nodes[0] = this.nodes[this.getLastIndex()]; + this.nodes.pop(); + this.heapifyDown(); + + return root; + } + + /** + * returns the root node in the heap + * @public + * @returns {HeapNode} + */ + root() { + if (this.size() === 0) return null; + return this.nodes[0]; + } + + /** + * returns the number of nodes in the heap + * @public + * @returns {number} + */ + size() { + return this.nodes.length; + } + + /** + * clears the heap + * @public + */ + clear() { + this.init(); + } + + /** + * convert a list of items into a heap + * @param {array} items + * @param {Class} HeapType + * @protected + * @static + * @returns {Heap} + */ + static heapify(items, HeapType) { + if (!isNoneEmptyArray(items)) return null; + + const heap = new HeapType(); + items.forEach((item) => { + if (isNumber(item) || isNoneEmptyString(item)) { + heap.insert(item); + } else if (isNoneNullObject(item) + && (isNumber(item.key) || isNoneEmptyString(item.key))) { + heap.insert(item.key, item.value); + } + }); + + return heap; + } +} + +module.exports = Heap; diff --git a/src/heapNode.js b/src/heapNode.js new file mode 100644 index 0000000..84043b0 --- /dev/null +++ b/src/heapNode.js @@ -0,0 +1,45 @@ +/** + * datastructures-js/heap + * @copyright 2019 Eyas Ranjous + * @license MIT + */ + +/** + * @class HeapNode + */ +class HeapNode { + constructor(key, value) { + this.key = key; + this.value = value; + } + + /** + * @public + * @returns {number|string} + */ + getKey() { + return this.key; + } + + /** + * @public + * @returns {object} + */ + getValue() { + return this.value; + } + + /** + * serialize heap node's properties + * @public + * @returns {object} + */ + serialize() { + return { + key: this.key, + value: this.value + }; + } +} + +module.exports = HeapNode; diff --git a/src/maxHeap.js b/src/maxHeap.js new file mode 100644 index 0000000..f25877a --- /dev/null +++ b/src/maxHeap.js @@ -0,0 +1,104 @@ +/** + * datastructures-js/heap + * @copyright 2019 Eyas Ranjous + * @license MIT + */ + +const Heap = require('./heap'); + +/** + * @class MaxHeap + * @extends Heap + */ +class MaxHeap extends Heap { + /** + * checks if the max child should be swapped with its parent + * @protected + * @returns {boolean} + */ + shouldSwap(maxChildIndex, parentIndex) { + const maxChild = this.nodes[maxChildIndex]; + const parent = this.nodes[parentIndex]; + return maxChild.getKey() > parent.getKey(); + } + + /** + * gets the max child's index of two node's children + * @param {number} leftChildIndex + * @param {number} rightChildIndex + * @privaye + * @returns {number} + */ + getMaxChildIndex(leftChildIndex, rightChildIndex) { + const leftChild = this.nodes[leftChildIndex]; + const rightChild = this.nodes[rightChildIndex]; + if (leftChild.getKey() > rightChild.getKey()) { + return leftChildIndex; + } + return rightChildIndex; + } + + /** + * returns the max child's index of two children before an index + * @private + * @param {number} i + * @param {number} leftChildIndex + * @param {number} rightChildIndex + * @returns {number} + */ + getMaxChildIndexBefore(i, leftChildIndex, rightChildIndex) { + const leftChild = this.nodes[leftChildIndex]; + const rightChild = this.nodes[rightChildIndex]; + if (leftChild.getKey() > rightChild.getKey() && leftChildIndex < i) { + return leftChildIndex; + } + return rightChildIndex; + } + + /** + * implements the parent's function to select a child's index + * @protected + * @override + * @param {number} leftChildIndex + * @param {number} rightChildIndex + * @returns {number} + */ + compareChildren(leftChildIndex, rightChildIndex) { + return this.getMaxChildIndex(leftChildIndex, rightChildIndex); + } + + /** + * implements the parent's function to select a child's index before an index + * @protected + * @override + * @param {number} i + * @param {number} leftChildIndex + * @param {number} rightChildIndex + * @returns {number} + */ + compareChildrenUntil(i, leftChildIndex, rightChildIndex) { + return this.getMaxChildIndexBefore(i, leftChildIndex, rightChildIndex); + } + + /** + * applies a shallow clone on the max heap + * @public + * @returns {MinHeap} + */ + clone() { + return super.clone(MaxHeap); + } + + /** + * builds a max heap from an array of items + * @param {array} items + * @public + * @static + * @returns {MaxHeap} + */ + static heapify(items) { + return super.heapify(items, MaxHeap); + } +} + +module.exports = MaxHeap; diff --git a/src/minHeap.js b/src/minHeap.js new file mode 100644 index 0000000..10ba5a1 --- /dev/null +++ b/src/minHeap.js @@ -0,0 +1,92 @@ +/** + * datastructures-js/heap + * @copyright 2019 Eyas Ranjous + * @license MIT + */ + +const Heap = require('./heap'); + +/** + * @class MinHeap + * @extends Heap + */ +class MinHeap extends Heap { + /** + * checks if the min child should be swapped with its parent + * @protected + * @returns {boolean} + */ + shouldSwap(minChildIndex, parentIndex) { + const minChild = this.nodes[minChildIndex]; + const parent = this.nodes[parentIndex]; + return minChild.getKey() < parent.getKey(); + } + + /** + * gets the min child's index of two node's children + * @param {number} leftChildIndex + * @param {number} rightChildIndex + * @privaye + * @returns {number} + */ + getMinChildIndex(leftChildIndex, rightChildIndex) { + const leftChild = this.nodes[leftChildIndex]; + const rightChild = this.nodes[rightChildIndex]; + if (leftChild.getKey() < rightChild.getKey()) { + return leftChildIndex; + } + return rightChildIndex; + } + + /** + * implements the parent's function to select a child's index + * @protected + * @override + * @param {number} leftChildIndex + * @param {number} rightChildIndex + * @returns {number} + */ + compareChildren(leftChildIndex, rightChildIndex) { + return this.getMinChildIndex(leftChildIndex, rightChildIndex); + } + + /** + * implements the parent's function to select a child's index before an index + * @protected + * @override + * @param {number} i + * @param {number} leftChildIndex + * @param {number} rightChildIndex + * @returns {number} + */ + compareChildrenUntil(index, leftChildIndex, rightChildIndex) { + const leftChild = this.nodes[leftChildIndex]; + const rightChild = this.nodes[rightChildIndex]; + if (rightChild.getKey() < leftChild.getKey() && rightChildIndex < index) { + return rightChildIndex; + } + return leftChildIndex; + } + + /** + * applies a shallow clone on the min heap + * @public + * @returns {MinHeap} + */ + clone() { + return super.clone(MinHeap); + } + + /** + * builds a min heap from an array of items + * @param {array} items + * @public + * @static + * @returns {MinHeap} + */ + static heapify(items) { + return super.heapify(items, MinHeap); + } +} + +module.exports = MinHeap; diff --git a/test/maxHeap.test.js b/test/maxHeap.test.js new file mode 100644 index 0000000..1467c88 --- /dev/null +++ b/test/maxHeap.test.js @@ -0,0 +1,139 @@ +const { expect } = require('chai'); +const MaxHeap = require('../src/maxHeap'); + +describe('MaxHeap unit tests', () => { + const maxHeap = new MaxHeap(); + + describe('.insert(key, value)', () => { + it('should insert nodes into the heap', () => { + maxHeap.insert(50); + maxHeap.insert(80); + maxHeap.insert(30, 'something'); + maxHeap.insert(90); + maxHeap.insert(60, null); + maxHeap.insert(40); + maxHeap.insert(20, { name: 'test' }); + }); + }); + + describe('size()', () => { + it('should return the size of the heap', () => { + expect(maxHeap.size()).to.equal(7); + }); + }); + + describe('.sort()', () => { + it('should sort a copy of the heap\'s nodes in ascending order', () => { + expect(maxHeap.clone().sort().map((n) => n.serialize())) + .to.deep.equal([ + { key: 20, value: { name: 'test' } }, + { key: 30, value: 'something' }, + { key: 40, value: undefined }, + { key: 50, value: undefined }, + { key: 60, value: null }, + { key: 80, value: undefined }, + { key: 90, value: undefined } + ]); + }); + }); + + describe('.root()', () => { + it('should get the root of the heap', () => { + expect(maxHeap.root().serialize()).to.deep.equal({ + key: 90, + value: undefined + }); + }); + }); + + describe('.extractRoot()', () => { + it('should extract the root (max key) from the heap', () => { + expect(maxHeap.extractRoot().serialize()).to.deep.equal({ + key: 90, + value: undefined + }); + expect(maxHeap.size()).to.equal(6); + + expect(maxHeap.extractRoot().serialize()).to.deep.equal({ + key: 80, + value: undefined + }); + expect(maxHeap.size()).to.equal(5); + + expect(maxHeap.extractRoot().serialize()).to.deep.equal({ + key: 60, + value: null + }); + expect(maxHeap.size()).to.equal(4); + + expect(maxHeap.extractRoot().serialize()).to.deep.equal({ + key: 50, + value: undefined + }); + expect(maxHeap.size()).to.equal(3); + + expect(maxHeap.extractRoot().serialize()).to.deep.equal({ + key: 40, + value: undefined + }); + expect(maxHeap.size()).to.equal(2); + + expect(maxHeap.extractRoot().serialize()).to.deep.equal({ + key: 30, + value: 'something' + }); + expect(maxHeap.size()).to.equal(1); + + expect(maxHeap.extractRoot().serialize()).to.deep.equal({ + key: 20, + value: { name: 'test' } + }); + expect(maxHeap.size()).to.equal(0); + + expect(maxHeap.extractRoot()).to.equal(null); + expect(maxHeap.root()).to.equal(null); + expect(maxHeap.size()).to.equal(0); + }); + }); + + describe('.clear()', () => { + it('should clear the items in the heap', () => { + maxHeap.insert(50); + maxHeap.clear(); + expect(maxHeap.root()).to.equal(null); + expect(maxHeap.size()).to.equal(0); + }); + }); + + describe('.heapify(items)', () => { + it('should build a heap from a list of items', () => { + const items = [ + 'b', + 'x', + { key: 'r', value: 'something' }, + 't', + { key: 'a', value: null }, + 'c', + { key: 'm', value: { name: 'test' } } + ]; + const heap = MaxHeap.heapify(items); + + expect(heap.extractRoot().serialize()).to.deep.equal({ + key: 'x', + value: undefined + }); + expect(heap.size()).to.equal(6); + + expect(heap.root().serialize()).to.deep.equal({ + key: 't', + value: undefined + }); + expect(heap.root().getValue()).to.equal(undefined); + expect(heap.size()).to.equal(6); + }); + + it('should return null if items is not a none-empty array', () => { + expect(MaxHeap.heapify('test')).to.equal(null); + }); + }); +}); diff --git a/test/minHeap.test.js b/test/minHeap.test.js new file mode 100644 index 0000000..612f6b0 --- /dev/null +++ b/test/minHeap.test.js @@ -0,0 +1,139 @@ +const { expect } = require('chai'); +const MinHeap = require('../src/minHeap'); + +describe('MinHeap unit tests', () => { + const minHeap = new MinHeap(); + + describe('.insert(key, value)', () => { + it('should insert nodes into the heap', () => { + minHeap.insert(50); + minHeap.insert(80); + minHeap.insert(30, 'something'); + minHeap.insert(90); + minHeap.insert(60, null); + minHeap.insert(40); + minHeap.insert(20, { name: 'test' }); + }); + }); + + describe('size()', () => { + it('should return the size of the heap', () => { + expect(minHeap.size()).to.equal(7); + }); + }); + + describe('.sort()', () => { + it('should sort a copy of the heap\'s nodes in descending order', () => { + expect(minHeap.clone().sort().map((n) => n.serialize())) + .to.deep.equal([ + { key: 90, value: undefined }, + { key: 80, value: undefined }, + { key: 60, value: null }, + { key: 50, value: undefined }, + { key: 40, value: undefined }, + { key: 30, value: 'something' }, + { key: 20, value: { name: 'test' } } + ]); + }); + }); + + describe('.root()', () => { + it('should get the root of the heap', () => { + expect(minHeap.root().serialize()).to.deep.equal({ + key: 20, + value: { name: 'test' } + }); + }); + }); + + describe('.extractRoot()', () => { + it('should extract the root (min key) from the heap', () => { + expect(minHeap.extractRoot().serialize()).to.deep.equal({ + key: 20, + value: { name: 'test' } + }); + expect(minHeap.size()).to.equal(6); + + expect(minHeap.extractRoot().serialize()).to.deep.equal({ + key: 30, + value: 'something' + }); + expect(minHeap.size()).to.equal(5); + + expect(minHeap.extractRoot().serialize()).to.deep.equal({ + key: 40, + value: undefined + }); + expect(minHeap.size()).to.equal(4); + + expect(minHeap.extractRoot().serialize()).to.deep.equal({ + key: 50, + value: undefined + }); + expect(minHeap.size()).to.equal(3); + + expect(minHeap.extractRoot().serialize()).to.deep.equal({ + key: 60, + value: null + }); + expect(minHeap.size()).to.equal(2); + + expect(minHeap.extractRoot().serialize()).to.deep.equal({ + key: 80, + value: undefined + }); + expect(minHeap.size()).to.equal(1); + + expect(minHeap.extractRoot().serialize()).to.deep.equal({ + key: 90, + value: undefined + }); + expect(minHeap.size()).to.equal(0); + + expect(minHeap.extractRoot()).to.equal(null); + expect(minHeap.root()).to.equal(null); + expect(minHeap.size()).to.equal(0); + }); + }); + + describe('.clear()', () => { + it('should clear the items in the heap', () => { + minHeap.insert(50); + minHeap.clear(); + expect(minHeap.root()).to.equal(null); + expect(minHeap.size()).to.equal(0); + }); + }); + + describe('.heapify(items)', () => { + it('should build a heap from a list of items', () => { + const items = [ + 50, + 80, + { key: 30, value: 'something' }, + 90, + { key: 60, value: null }, + 40, + { key: 20, value: { name: 'test' } } + ]; + const heap = MinHeap.heapify(items); + + expect(heap.extractRoot().serialize()).to.deep.equal({ + key: 20, + value: { name: 'test' } + }); + expect(heap.size()).to.equal(6); + + expect(heap.root().serialize()).to.deep.equal({ + key: 30, + value: 'something' + }); + expect(heap.root().getValue()).to.equal('something'); + expect(heap.size()).to.equal(6); + }); + + it('should return null if items is not a none-empty array', () => { + expect(MinHeap.heapify('test')).to.equal(null); + }); + }); +}); From 0a5aa6cc7e567bdaca73f9a799ab1cc9143317dc Mon Sep 17 00:00:00 2001 From: Eyas Ranjous Date: Sun, 15 Dec 2019 18:08:48 -0600 Subject: [PATCH 2/8] Update README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3dcd697..e4d43b8 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,12 @@ # @datastructures-js/heap -Javscript implementation for the Min/Max Heap Data Structures & Heap Sort Algorithm. - [![build:?](https://travis-ci.org/datastructures-js/heap.svg?branch=master)](https://travis-ci.org/datastructures-js/heap) [![npm](https://img.shields.io/npm/v/@datastructures-js/heap.svg)](https://www.npmjs.com/package/@datastructures-js/heap) [![npm](https://img.shields.io/npm/dm/@datastructures-js/heap.svg)](https://www.npmjs.com/package/@datastructures-js/heap) [![npm](https://img.shields.io/badge/node-%3E=%206.0-blue.svg)](https://www.npmjs.com/package/@datastructures-js/heap) +Javscript implementation for the Min/Max Heap Data Structures & Heap Sort Algorithm. + +![heap](https://user-images.githubusercontent.com/6517308/70871547-bd852900-1f65-11ea-909f-86f4d090f152.jpg) + # API ## install From 05b18dd7f2bd86a034713ce4ac05a36c9112f879 Mon Sep 17 00:00:00 2001 From: Eyas Ranjous Date: Sun, 15 Dec 2019 18:13:03 -0600 Subject: [PATCH 3/8] spaces and mispelling --- src/heap.js | 10 ++++++++-- src/maxHeap.js | 4 +++- src/minHeap.js | 4 +++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/heap.js b/src/heap.js index 75135b7..9edd2d7 100644 --- a/src/heap.js +++ b/src/heap.js @@ -103,6 +103,7 @@ class Heap { heapifyUp() { let childIndex = this.getLastIndex(); let parentIndex = this.getParentIndex(childIndex); + while (childIndex > 0 && this.shouldSwap(childIndex, parentIndex)) { this.swap(childIndex, parentIndex); childIndex = parentIndex; @@ -117,6 +118,7 @@ class Heap { heapifyDown() { let parentIndex = 0; let childIndex = this.compareChildrenOf(parentIndex); + while (childIndex > 0 && this.shouldSwap(childIndex, parentIndex)) { this.swap(childIndex, parentIndex); parentIndex = childIndex; @@ -129,14 +131,17 @@ class Heap { * @param {number} i - swapped node's index * @private */ - heapifyDownUntil(i) { + heapifyDownUntil(index) { let parentIndex = 0; let leftChildIndex = 1; let rightChildIndex = 2; let childIndex; + while (rightChildIndex < i) { childIndex = this.compareChildrenUntil( - i, leftChildIndex, rightChildIndex + index, + leftChildIndex, + rightChildIndex ); if (this.shouldSwap(childIndex, parentIndex)) { @@ -159,6 +164,7 @@ class Heap { this.swap(0, i); this.heapifyDownUntil(i); } + return this.nodes; } diff --git a/src/maxHeap.js b/src/maxHeap.js index f25877a..faf2101 100644 --- a/src/maxHeap.js +++ b/src/maxHeap.js @@ -24,9 +24,9 @@ class MaxHeap extends Heap { /** * gets the max child's index of two node's children + * @private * @param {number} leftChildIndex * @param {number} rightChildIndex - * @privaye * @returns {number} */ getMaxChildIndex(leftChildIndex, rightChildIndex) { @@ -83,6 +83,7 @@ class MaxHeap extends Heap { /** * applies a shallow clone on the max heap * @public + * @override * @returns {MinHeap} */ clone() { @@ -93,6 +94,7 @@ class MaxHeap extends Heap { * builds a max heap from an array of items * @param {array} items * @public + * @override * @static * @returns {MaxHeap} */ diff --git a/src/minHeap.js b/src/minHeap.js index 10ba5a1..f06e6c1 100644 --- a/src/minHeap.js +++ b/src/minHeap.js @@ -24,9 +24,9 @@ class MinHeap extends Heap { /** * gets the min child's index of two node's children + * @private * @param {number} leftChildIndex * @param {number} rightChildIndex - * @privaye * @returns {number} */ getMinChildIndex(leftChildIndex, rightChildIndex) { @@ -71,6 +71,7 @@ class MinHeap extends Heap { /** * applies a shallow clone on the min heap * @public + * @override * @returns {MinHeap} */ clone() { @@ -81,6 +82,7 @@ class MinHeap extends Heap { * builds a min heap from an array of items * @param {array} items * @public + * @override * @static * @returns {MinHeap} */ From 3b47296bfce6a5a6b68b64da209b6514a9f33b75 Mon Sep 17 00:00:00 2001 From: Eyas Ranjous Date: Sun, 15 Dec 2019 18:20:06 -0600 Subject: [PATCH 4/8] consistent interface and clean spaces Update README.md Update README.md Update README.md Update README.md finish README Update README.md Update README.md update README Update README.md Update README.md Update README.md Update README.md Update README.md Update README.md Update README.md --- README.md | 144 ++++++++++++++++++++++++++++++++++++++----------- src/heap.js | 4 +- src/maxHeap.js | 12 ++--- src/minHeap.js | 29 +++++++--- 4 files changed, 142 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index e4d43b8..fc66cde 100644 --- a/README.md +++ b/README.md @@ -3,18 +3,17 @@ [![npm](https://img.shields.io/npm/v/@datastructures-js/heap.svg)](https://www.npmjs.com/package/@datastructures-js/heap) [![npm](https://img.shields.io/npm/dm/@datastructures-js/heap.svg)](https://www.npmjs.com/package/@datastructures-js/heap) [![npm](https://img.shields.io/badge/node-%3E=%206.0-blue.svg)](https://www.npmjs.com/package/@datastructures-js/heap) -Javscript implementation for the Min/Max Heap Data Structures & Heap Sort Algorithm. +a complete javascript implementation for the Min/Max Heap data structures & Heap Sort algorithm. ![heap](https://user-images.githubusercontent.com/6517308/70871547-bd852900-1f65-11ea-909f-86f4d090f152.jpg) -# API - ## install ```sh npm install --save @datastructures-js/heap ``` -## require/import +## API + ### require ```js const { MinHeap, MaxHeap } = require('@datastructures-js/heap'); @@ -25,29 +24,52 @@ const { MinHeap, MaxHeap } = require('@datastructures-js/heap'); import { MinHeap, MaxHeap } from '@datastructures-js/heap'; ``` -## Create a Heap +### create a heap + +#### new +creates an empty heap. -### Create an empty instance ```js const minHeap = new MinHeap(); const maxHeap = new MaxHeap(); ``` -### .heapify(list) -A static function that converts an array of items into a heap structure. The list items can be **number**, **string**, or a serialized heap node like `{ key: 10, value: { someProp: 'someVal' } }`. +#### .heapify(list) +converts an array of objects to a heap. + +the function can read a list elements that are **number**, **string**, or a **serialized heap node** like `{ key: 10, value: { someProp: 'someVal' } }`. ```js -const list = [50, 30, 80, 1, 7, 8]; +const list = [ + 50, + 80, + { key: 30, value: 'something' }, + 90, + { key: 60, value: null }, + 40, + { key: 20, value: { name: 'test' } } +]; const minHeap = MinHeap.heapify(list); const maxHeap = MaxHeap.heapify(list); ``` -## .insert(key, value) -insert a node into the heap. Heap nodes are instances of the class NodeHeap. +### .insert(key, value) +insert a node into the heap. + +**key** can be a **number** or a **string** + +**value** can be any **object** type. + +a heap node is created as an instance of **NodeHeap**. + ```js +const minHeap = new MinHeap(); + +const maxHeap = new MaxHeap(); + minHeap.insert(50); minHeap.insert(80); minHeap.insert(30, 'something'); @@ -55,55 +77,115 @@ minHeap.insert(90); minHeap.insert(60, null); minHeap.insert(40); minHeap.insert(20, { name: 'test' }); + +maxHeap.insert(50); +maxHeap.insert(80); +maxHeap.insert(30, 'something'); +maxHeap.insert(90); +maxHeap.insert(60, null); +maxHeap.insert(40); +maxHeap.insert(20, { name: 'test' }); ``` -## .root() -returns (peeks) the root node without removing it. The returned node is an instance of **NodeHeap** class which implements the following interface: +### HeapNode +returned with .root() & .extractRoot() functions. It implements the following interface -### .getKey() -returns the node's key (number or string) that is used to compare with other nodes key. +#### .getKey() +returns the node's key (number or string) that is used to compare with other. -### .getValue() -returns the value that is associated with the key. Value can be any object type. +#### .getValue() +returns the value that is associated with the key. -### .serialize() +#### .serialize() returns an object literal of key/value of the node. +### .root() +returns (peeks) the root node without removing it. + ```js const min = minHeap.root(); - console.log(min.getKey()); // 20 console.log(min.getValue()); // { name: 'test' } console.log(min.serialize()); // { key: 20, value: { name: 'test' } } + +const max = maxHeap.root(); +console.log(max.getKey()); // 90 +console.log(max.getValue()); // undefined +console.log(max.serialize()); // { key: 90, value: undefined } ``` -## .extractRoot() +### .extractRoot() returns and remove the root node in the heap. ```js const min = minHeap.extractRoot(); - console.log(min.getKey()); // 20 console.log(min.getValue()); // { name: 'test' } console.log(min.serialize()); // { key: 20, value: { name: 'test' } } - console.log(minHeap.root().getKey()); // 30 + +const max = maxHeap.extractRoot(); +console.log(max.getKey()); // 90 +console.log(max.getValue()); // undefined +console.log(max.serialize()); // { key: 20, value: undefined } +console.log(maxHeap.root().getKey()); // 80 ``` -## .size() -returns the number of nodes in the heap +### .size() +returns the number of nodes in the heap. + ```js console.log(minHeap.size()); // 6 +console.log(maxHeap.size()); // 6 ``` -## .sort() -Sorting is implemented for both Max Heap (sorting keys in ascending order) & Min Heap (sorting keys in descending order). Calling sort on the heap directly mutates its nodes order. To avoid that and sort a heap while maintaining the structure, you can sort a clone. +### .clone() +creates a shallow copy of a heap by slicing the nodes array and passing it to a new heap instance. ```js -const sortedDesc = minHeap.clone().sort(); -const sortedAsc = maxHeap.clone().sort(); +const minHeapClone = minHeap.clone(); +minHeapClone.extractRoot(); + +console.log(minHeapClone.root().getKey()); // 40 +console.log(minHeap.root().getKey()); // 30 +``` + +### .sort() +implements Heap Sort and sorts a heap in ascending order for Max Heap or descending order for Min Heap. + +calling .sort() directly on a heap will mutate its nodes location. To avoid that, you can sort a shallow copy of the heap. + +```js +const sortedAsc = maxHeap.clone().sort(); // does not mutate the heap structure +const sortedDesc = minHeap.clone().sort(); // does not mutate the heap structure +``` + +If you are using this npm for the purpose of sorting a list of elements using Heap Sort, you can do it like this + +```js +const sortedAsc = MaxHeap.heapify(unsortedList).sort(); +const sortedDesc = MinHeap.heapify(unsortedList).sort(); +``` + +### .clear() +clears the nodes in the heap + +```js +minHeap.clear(); +maxHeap.clear(); + +console.log(minHeap.size()); // 0 +console.log(minHeap.root()); // null + +console.log(maxHeap.size()); // 0 +console.log(maxHeap.root()); // null +``` + +## Build +lint + tests +``` +grunt build ``` -## .clear() -## .clone() -Creates a shallow clone of a heap by slicing the nodes array and passing it to a new heap instance. +## License +The MIT License. Full License is [here](https://github.com/datastructures-js/heap/blob/master/LICENSE) diff --git a/src/heap.js b/src/heap.js index 9edd2d7..fca1b23 100644 --- a/src/heap.js +++ b/src/heap.js @@ -137,8 +137,8 @@ class Heap { let rightChildIndex = 2; let childIndex; - while (rightChildIndex < i) { - childIndex = this.compareChildrenUntil( + while (rightChildIndex < index) { + childIndex = this.compareChildrenBefore( index, leftChildIndex, rightChildIndex diff --git a/src/maxHeap.js b/src/maxHeap.js index faf2101..e899c3c 100644 --- a/src/maxHeap.js +++ b/src/maxHeap.js @@ -41,15 +41,15 @@ class MaxHeap extends Heap { /** * returns the max child's index of two children before an index * @private - * @param {number} i + * @param {number} index * @param {number} leftChildIndex * @param {number} rightChildIndex * @returns {number} */ - getMaxChildIndexBefore(i, leftChildIndex, rightChildIndex) { + getMaxChildIndexBefore(index, leftChildIndex, rightChildIndex) { const leftChild = this.nodes[leftChildIndex]; const rightChild = this.nodes[rightChildIndex]; - if (leftChild.getKey() > rightChild.getKey() && leftChildIndex < i) { + if (leftChild.getKey() > rightChild.getKey() && leftChildIndex < index) { return leftChildIndex; } return rightChildIndex; @@ -71,13 +71,13 @@ class MaxHeap extends Heap { * implements the parent's function to select a child's index before an index * @protected * @override - * @param {number} i + * @param {number} index * @param {number} leftChildIndex * @param {number} rightChildIndex * @returns {number} */ - compareChildrenUntil(i, leftChildIndex, rightChildIndex) { - return this.getMaxChildIndexBefore(i, leftChildIndex, rightChildIndex); + compareChildrenBefore(index, leftChildIndex, rightChildIndex) { + return this.getMaxChildIndexBefore(index, leftChildIndex, rightChildIndex); } /** diff --git a/src/minHeap.js b/src/minHeap.js index f06e6c1..fc41a5a 100644 --- a/src/minHeap.js +++ b/src/minHeap.js @@ -38,6 +38,24 @@ class MinHeap extends Heap { return rightChildIndex; } + /** + * returns the min child's index of two children before an index + * @private + * @param {number} index + * @param {number} leftChildIndex + * @param {number} rightChildIndex + * @returns {number} + */ + getMinChildIndexBefore(index, leftChildIndex, rightChildIndex) { + const leftChild = this.nodes[leftChildIndex]; + const rightChild = this.nodes[rightChildIndex]; + if (rightChild.getKey() < leftChild.getKey() && rightChildIndex < index) { + return rightChildIndex; + } + return leftChildIndex; + } + + /** * implements the parent's function to select a child's index * @protected @@ -54,18 +72,13 @@ class MinHeap extends Heap { * implements the parent's function to select a child's index before an index * @protected * @override - * @param {number} i + * @param {number} index * @param {number} leftChildIndex * @param {number} rightChildIndex * @returns {number} */ - compareChildrenUntil(index, leftChildIndex, rightChildIndex) { - const leftChild = this.nodes[leftChildIndex]; - const rightChild = this.nodes[rightChildIndex]; - if (rightChild.getKey() < leftChild.getKey() && rightChildIndex < index) { - return rightChildIndex; - } - return leftChildIndex; + compareChildrenBefore(index, leftChildIndex, rightChildIndex) { + return this.getMinChildIndexBefore(index, leftChildIndex, rightChildIndex); } /** From 1a9f55a96203b63ef25f3010e659ceb3259165aa Mon Sep 17 00:00:00 2001 From: Eyas Ranjous Date: Sun, 15 Dec 2019 20:37:10 -0600 Subject: [PATCH 5/8] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fc66cde..d8d03e0 100644 --- a/README.md +++ b/README.md @@ -151,7 +151,7 @@ console.log(minHeap.root().getKey()); // 30 ``` ### .sort() -implements Heap Sort and sorts a heap in ascending order for Max Heap or descending order for Min Heap. +implements Heap Sort and sorts a **Max Heap in ascneding order** or a **Min Heap in descending order**. calling .sort() directly on a heap will mutate its nodes location. To avoid that, you can sort a shallow copy of the heap. From 03b099860ac70a52b64e0cb929c6ec3c2f6e352b Mon Sep 17 00:00:00 2001 From: Eyas Ranjous Date: Sun, 15 Dec 2019 20:49:55 -0600 Subject: [PATCH 6/8] consistent code --- src/heap.js | 2 +- src/maxHeap.js | 6 +++--- src/minHeap.js | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/heap.js b/src/heap.js index fca1b23..54d0e4b 100644 --- a/src/heap.js +++ b/src/heap.js @@ -137,7 +137,7 @@ class Heap { let rightChildIndex = 2; let childIndex; - while (rightChildIndex < index) { + while (leftChildIndex < index) { childIndex = this.compareChildrenBefore( index, leftChildIndex, diff --git a/src/maxHeap.js b/src/maxHeap.js index e899c3c..2fc3b5c 100644 --- a/src/maxHeap.js +++ b/src/maxHeap.js @@ -49,10 +49,10 @@ class MaxHeap extends Heap { getMaxChildIndexBefore(index, leftChildIndex, rightChildIndex) { const leftChild = this.nodes[leftChildIndex]; const rightChild = this.nodes[rightChildIndex]; - if (leftChild.getKey() > rightChild.getKey() && leftChildIndex < index) { - return leftChildIndex; + if (rightChild.getKey() > leftChild.getKey() && rightChildIndex < index) { + return rightChildIndex; } - return rightChildIndex; + return leftChildIndex; } /** diff --git a/src/minHeap.js b/src/minHeap.js index fc41a5a..c24acbe 100644 --- a/src/minHeap.js +++ b/src/minHeap.js @@ -55,7 +55,6 @@ class MinHeap extends Heap { return leftChildIndex; } - /** * implements the parent's function to select a child's index * @protected From 1b95a3667087aa6afcf2ad91a27dfbca4789f674 Mon Sep 17 00:00:00 2001 From: Eyas Ranjous Date: Sun, 15 Dec 2019 20:54:57 -0600 Subject: [PATCH 7/8] CHANGELOG v1.0.0 --- CHANGELOG.md | 11 +++++++++++ package.json | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f858589 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [1.0.0] - 2019-12-15 +### Added +- initial release diff --git a/package.json b/package.json index 22ae806..20175ff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@datastructures-js/heap", - "version": "0.0.0", + "version": "1.0.0", "description": "Heap (min & max) implementation in javascript", "main": "index.js", "scripts": { From b85fd9a73ef3c8d40997319fcfc5df740e289bd5 Mon Sep 17 00:00:00 2001 From: Eyas Ranjous Date: Sun, 15 Dec 2019 21:05:54 -0600 Subject: [PATCH 8/8] v1.0.0 --- src/heap.js | 5 +++-- src/maxHeap.js | 2 +- src/minHeap.js | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/heap.js b/src/heap.js index 54d0e4b..8ab3352 100644 --- a/src/heap.js +++ b/src/heap.js @@ -169,8 +169,9 @@ class Heap { } /** - * applies a shallow clone on a heap + * returns a shallow copy of a heap * @protected + * @param {class} HeapType * @returns {Heap} */ clone(HeapType) { @@ -236,7 +237,7 @@ class Heap { /** * convert a list of items into a heap * @param {array} items - * @param {Class} HeapType + * @param {class} HeapType * @protected * @static * @returns {Heap} diff --git a/src/maxHeap.js b/src/maxHeap.js index 2fc3b5c..d1b7e1c 100644 --- a/src/maxHeap.js +++ b/src/maxHeap.js @@ -81,7 +81,7 @@ class MaxHeap extends Heap { } /** - * applies a shallow clone on the max heap + * returns a shallow copy of a max heap * @public * @override * @returns {MinHeap} diff --git a/src/minHeap.js b/src/minHeap.js index c24acbe..ddc5cc1 100644 --- a/src/minHeap.js +++ b/src/minHeap.js @@ -81,7 +81,7 @@ class MinHeap extends Heap { } /** - * applies a shallow clone on the min heap + * returns a shallow copy of a min heap * @public * @override * @returns {MinHeap}