diff --git a/src/_DataStructures_/Heaps/MaxHeap/MaxHeap.test.js b/src/_DataStructures_/Heaps/MaxHeap/MaxHeap.test.js new file mode 100644 index 00000000..181390ed --- /dev/null +++ b/src/_DataStructures_/Heaps/MaxHeap/MaxHeap.test.js @@ -0,0 +1,50 @@ +const MaxHeap = require('.'); + +describe('MaxHeap', () => { + it('Should be a class', () => { + expect(typeof MaxHeap.prototype.constructor).toEqual('function'); + }); + + const mh = new MaxHeap(); + + beforeEach(() => { + mh.destroy(); + }); + + it('Should create an instance of MaxHeap', () => { + expect(mh instanceof MaxHeap).toEqual(true); + }); + + it('Should add an element to the MaxHeap', () => { + mh.add(10); + expect(mh.getMax()).toEqual(10); + }); + + it('Should keep the largest element at the root', () => { + [12, 5, 34].forEach(el => mh.add(el)); + expect(mh.getMax()).toEqual(34); + }); + + it('Should retain Heap properties after removal of an element', () => { + [12, 45, 1, 34].forEach(el => mh.add(el)); + expect(mh.getMax()).toEqual(45); + mh.remove(); + expect(mh.getMax()).toEqual(34); + }); + + it('Should return `null` when heap is empty', () => { + [1, 34].forEach(el => mh.add(el)); + expect(mh.getMax()).toEqual(34); + mh.remove(); + mh.remove(); + expect(mh.getMax()).toEqual(null); + }); + + it('Should return the elelment value on `remove()`', () => { + [1, 34].forEach(el => mh.add(el)); + expect(mh.getMax()).toEqual(34); + expect(mh.remove()).toEqual(34); + expect(mh.remove()).toEqual(1); + expect(mh.getMax()).toEqual(null); + }); +}); diff --git a/src/_DataStructures_/Heaps/MaxHeap/index.js b/src/_DataStructures_/Heaps/MaxHeap/index.js new file mode 100644 index 00000000..bf94e9d6 --- /dev/null +++ b/src/_DataStructures_/Heaps/MaxHeap/index.js @@ -0,0 +1,78 @@ +class MaxHeap { + constructor() { + this.heap = []; + } + + add(element) { + this.heap.push(element); + // check for the parent element & swap if required + // eslint-disable-next-line no-underscore-dangle + this.__traverseUpAndSwap(this.heap.length - 1); + } + + getMax() { + return this.heap[0] || null; + } + + remove() { + // return the element at the root + const max = this.heap[0] || null; + if (this.heap.length > 1) { + // move the leaf to the root + this.heap[0] = this.heap[this.heap.length - 1]; + this.heap.splice(this.heap.length - 1, 1); + // restore the heapify property + // eslint-disable-next-line no-underscore-dangle + this.__heapify(0); + return max; + } + + if (this.heap.length === 1) { + this.heap.splice(this.heap.length - 1, 1); + return max; + } + + return max; + } + + __heapify(index) { + const left = index * 2; + const right = index * 2 + 1; + let largest = index; + + if (this.heap.length > left && this.heap[largest] < this.heap[left]) { + largest = left; + } + + if (this.heap.length > right && this.heap[largest] < this.heap[right]) { + largest = right; + } + + if (largest !== index) { + const temp = this.heap[largest]; + this.heap[largest] = this.heap[index]; + this.heap[index] = temp; + // eslint-disable-next-line no-underscore-dangle + this.__heapify(largest); + } + } + + __traverseUpAndSwap(index) { + if (index <= 0) return; + const parent = Math.floor(index / 2); + + if (this.heap[parent] < this.heap[index]) { + const temp = this.heap[parent]; + this.heap[parent] = this.heap[index]; + this.heap[index] = temp; + // eslint-disable-next-line no-underscore-dangle + this.__traverseUpAndSwap(parent); + } + } + + destroy() { + this.heap = []; + } +} + +module.exports = MaxHeap;