From 09100eab442f416bb043dd8e7e6f05736412dc98 Mon Sep 17 00:00:00 2001 From: Oleksii Trekhleb Date: Fri, 30 Mar 2018 08:03:26 +0300 Subject: [PATCH] Add Heap. --- README.md | 1 + src/data-structures/heap/MinHeap.js | 118 ++++++++++++++++++ src/data-structures/heap/README.md | 1 + .../heap/__test__/MinHeap.test.js | 56 +++++++++ 4 files changed, 176 insertions(+) create mode 100644 src/data-structures/heap/MinHeap.js create mode 100644 src/data-structures/heap/README.md create mode 100644 src/data-structures/heap/__test__/MinHeap.test.js diff --git a/README.md b/README.md index d5e6a3ab..00f42a22 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ 2. [Queue](https://github.com/trekhleb/javascript-algorithms/tree/master/src/data-structures/queue) 3. [Stack](https://github.com/trekhleb/javascript-algorithms/tree/master/src/data-structures/stack) 4. [Hash Table](https://github.com/trekhleb/javascript-algorithms/tree/master/src/data-structures/hash-table) +5. [Heap](https://github.com/trekhleb/javascript-algorithms/tree/master/src/data-structures/heap) ## [Algorithms](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms) diff --git a/src/data-structures/heap/MinHeap.js b/src/data-structures/heap/MinHeap.js new file mode 100644 index 00000000..73e2a22d --- /dev/null +++ b/src/data-structures/heap/MinHeap.js @@ -0,0 +1,118 @@ +export default class MinHeap { + constructor() { + // Array representation of the heap. + this.heapContainer = []; + } + + static getLeftChildIndex(parentIndex) { + return (2 * parentIndex) + 1; + } + + static getRightChildIndex(parentIndex) { + return (2 * parentIndex) + 2; + } + + static getParentIndex(childIndex) { + return Math.floor((childIndex - 1) / 2); + } + + static hasParent(childIndex) { + return this.getParentIndex(childIndex) >= 0; + } + + hasLeftChild(parentIndex) { + return MinHeap.getLeftChildIndex(parentIndex) < this.heapContainer.length; + } + + hasRightChild(parentIndex) { + return MinHeap.getRightChildIndex(parentIndex) < this.heapContainer.length; + } + + leftChild(parentIndex) { + return this.heapContainer[MinHeap.getLeftChildIndex(parentIndex)]; + } + + rightChild(parentIndex) { + return this.heapContainer[MinHeap.getRightChildIndex(parentIndex)]; + } + + parent(childIndex) { + return this.heapContainer[MinHeap.getParentIndex(childIndex)]; + } + + swap(indexOne, indexTwo) { + const tmp = this.heapContainer[indexTwo]; + this.heapContainer[indexTwo] = this.heapContainer[indexOne]; + this.heapContainer[indexOne] = tmp; + } + + peek() { + if (this.heapContainer.length === 0) { + return null; + } + + return this.heapContainer[0]; + } + + poll() { + if (this.heapContainer.length === 0) { + return null; + } + + if (this.heapContainer.length === 1) { + return this.heapContainer.pop(); + } + + const item = this.heapContainer[0]; + + // Move the last element from the end to the head. + this.heapContainer[0] = this.heapContainer.pop(); + this.heapifyDown(); + + return item; + } + + add(item) { + this.heapContainer.push(item); + this.heapifyUp(); + } + + heapifyUp() { + let currentIndex = this.heapContainer.length - 1; + + while ( + MinHeap.hasParent(currentIndex) && + this.parent(currentIndex) > this.heapContainer[currentIndex] + ) { + this.swap(currentIndex, MinHeap.getParentIndex(currentIndex)); + currentIndex = MinHeap.getParentIndex(currentIndex); + } + } + + heapifyDown() { + let currentIndex = 0; + let nextIndex = 0; + + while (this.hasLeftChild(currentIndex)) { + if ( + this.hasRightChild(currentIndex) && + this.leftChild(currentIndex) > this.rightChild(currentIndex) + ) { + nextIndex = MinHeap.getRightChildIndex(currentIndex); + } else { + nextIndex = MinHeap.getLeftChildIndex(currentIndex); + } + + if (this.heapContainer[currentIndex] < this.heapContainer[nextIndex]) { + break; + } + + this.swap(currentIndex, nextIndex); + currentIndex = nextIndex; + } + } + + toString() { + return this.heapContainer.toString(); + } +} diff --git a/src/data-structures/heap/README.md b/src/data-structures/heap/README.md new file mode 100644 index 00000000..fdf00083 --- /dev/null +++ b/src/data-structures/heap/README.md @@ -0,0 +1 @@ +# Heap diff --git a/src/data-structures/heap/__test__/MinHeap.test.js b/src/data-structures/heap/__test__/MinHeap.test.js new file mode 100644 index 00000000..64963de2 --- /dev/null +++ b/src/data-structures/heap/__test__/MinHeap.test.js @@ -0,0 +1,56 @@ +import MinHeap from '../MinHeap'; + +describe('MinHeap', () => { + it('should create an empty min heap', () => { + const minHeap = new MinHeap(); + + expect(minHeap).toBeDefined(); + expect(minHeap.peek()).toBeNull(); + }); + + it('should add items to the heap and heapify it up', () => { + const minHeap = new MinHeap(); + + minHeap.add(5); + expect(minHeap.peek()).toBe(5); + expect(minHeap.toString()).toBe('5'); + + minHeap.add(3); + expect(minHeap.peek()).toBe(3); + expect(minHeap.toString()).toBe('3,5'); + + minHeap.add(10); + expect(minHeap.peek()).toBe(3); + expect(minHeap.toString()).toBe('3,5,10'); + + minHeap.add(1); + expect(minHeap.peek()).toBe(1); + expect(minHeap.toString()).toBe('1,3,10,5'); + }); + + it('should poll items from the heap and heapify it down', () => { + const minHeap = new MinHeap(); + + minHeap.add(5); + minHeap.add(3); + minHeap.add(10); + minHeap.add(1); + + expect(minHeap.toString()).toBe('1,3,10,5'); + + expect(minHeap.poll()).toBe(1); + expect(minHeap.toString()).toBe('3,5,10'); + + expect(minHeap.poll()).toBe(3); + expect(minHeap.toString()).toBe('5,10'); + + expect(minHeap.poll()).toBe(5); + expect(minHeap.toString()).toBe('10'); + + expect(minHeap.poll()).toBe(10); + expect(minHeap.toString()).toBe(''); + + expect(minHeap.poll()).toBeNull(); + expect(minHeap.toString()).toBe(''); + }); +});