From 4b8e6e9f61d0dceaef77eb92da38651c05c48e31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A4ghib=20Hasan?= Date: Sat, 20 Apr 2024 20:10:54 +0300 Subject: [PATCH 1/2] add solution for heap --- readme.md | 3 +- src/data-structures/heap/heap.test.ts | 12 ++ src/data-structures/heap/heap.ts | 197 ++++++++++++++++++++++++++ src/data-structures/heap/notes.md | 19 +++ 4 files changed, 230 insertions(+), 1 deletion(-) create mode 100644 src/data-structures/heap/heap.test.ts create mode 100644 src/data-structures/heap/heap.ts create mode 100644 src/data-structures/heap/notes.md diff --git a/readme.md b/readme.md index 5f858a0..6acdb9d 100644 --- a/readme.md +++ b/readme.md @@ -173,7 +173,7 @@ -- -## Data-Structures (5 / -) +## Data-Structures (6 / -) | Name | Solution | | ------------------ | ------------------------------------------------------ | @@ -182,6 +182,7 @@ | Stack | [TypeScript](./src/data-structures/stack) | | Hash Table | [TypeScript](./src/data-structures/hash-table) | | Queue | [TypeScript](./src/data-structures/queue) | +| Heap | [TypeScript](./src/data-structures/heap) | ## License diff --git a/src/data-structures/heap/heap.test.ts b/src/data-structures/heap/heap.test.ts new file mode 100644 index 0000000..73c067d --- /dev/null +++ b/src/data-structures/heap/heap.test.ts @@ -0,0 +1,12 @@ +import Heap from './heap' + +describe('Heap', () => { + it('should not allow creating an instance directly', () => { + const instantiateHeap = () => { + const heap = new Heap() + heap.add(1) + } + + expect(instantiateHeap).toThrowError() + }) +}) diff --git a/src/data-structures/heap/heap.ts b/src/data-structures/heap/heap.ts new file mode 100644 index 0000000..739239e --- /dev/null +++ b/src/data-structures/heap/heap.ts @@ -0,0 +1,197 @@ +import Comparator from '../utils/comparator' + +export default class Heap { + public heapContainer: T[] + public compare: Comparator + + constructor(comparatorFunction?: Function) { + if (new.target === Heap) { + throw new TypeError('Cannot construct Heap instances directly') + } + + this.heapContainer = [] + this.compare = new Comparator(comparatorFunction) + } + + getLeftChildIndex(parentIndex: number) { + return 2 * parentIndex + 1 + } + + getRightChildIndex(parentIndex: number) { + return 2 * parentIndex + 2 + } + + getParentIndex(childIndex: number) { + /* Math.floor - Returns the greatest integer less than or equal to its numeric argument. */ + return Math.floor((childIndex - 1) / 2) + } + + hasParent(childIndex: number) { + return this.getParentIndex(childIndex) >= 0 + } + + hasLeftChild(parentIndex: number) { + return this.getLeftChildIndex(parentIndex) < this.heapContainer.length + } + + hasRightChild(parentIndex: number) { + return this.getRightChildIndex(parentIndex) < this.heapContainer.length + } + + leftChild(parentIndex: number) { + return this.heapContainer[this.getLeftChildIndex(parentIndex)] + } + + rightChild(parentIndex: number) { + return this.heapContainer[this.getRightChildIndex(parentIndex)] + } + + parent(childIndex: number) { + return this.heapContainer[this.getParentIndex(childIndex)] + } + + swap(indexOne: number, indexTwo: number) { + 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] + } + + pool() { + if (this.heapContainer.length === 0) { + return null + } + + if (this.heapContainer.length === 1) { + return this.heapContainer.pop() + } + + const item = this.heapContainer[0] + + this.heapContainer[0] = this.heapContainer.pop() as T + this.heapifyDown() + + return item + } + + add(item: T) { + this.heapContainer.push(item) + this.heapifyUp() + return this + } + + remove(item: T, comparator = this.compare) { + const numberOfItemsToRemove = this.find(item, comparator).length + + for (let iteration = 0; iteration < numberOfItemsToRemove; iteration += 1) { + const indexToRemove = this.find(item, comparator).pop()! + + if (indexToRemove === this.heapContainer.length - 1) { + this.heapContainer.pop() + } else { + this.heapContainer[indexToRemove] = this.heapContainer.pop()! + + const parentItem = this.parent(indexToRemove) + + if ( + this.hasLeftChild(indexToRemove) && + (!parentItem || + this.pairsIsInCorrectOrder( + parentItem, + this.heapContainer[indexToRemove] + )) + ) { + this.heapifyDown(indexToRemove) + } else { + this.heapifyUp(indexToRemove) + } + } + } + + return this + } + + find(item: T, comparator = this.compare) { + const foundItemIndices = [] + + for ( + let itemIndex = 0; + itemIndex < this.heapContainer.length; + itemIndex += 1 + ) { + if (comparator.equal(item, this.heapContainer[itemIndex])) { + foundItemIndices.push(itemIndex) + } + } + + return foundItemIndices + } + + isEmpty() { + return !this.heapContainer.length + } + + toString() { + return this.heapContainer.toString() + } + + heapifyUp(customStartIndex = 0) { + let currentIndex = customStartIndex || this.heapContainer.length - 1 + + while ( + this.hasParent(currentIndex) && + !this.pairsIsInCorrectOrder( + this.parent(currentIndex), + this.heapContainer[currentIndex] + ) + ) { + this.swap(currentIndex, this.getParentIndex(currentIndex)) + currentIndex = this.getParentIndex(currentIndex) + } + } + + heapifyDown(customStartIndex = 0) { + let currentIndex = customStartIndex + let nextIndex = null + + while (this.hasLeftChild(currentIndex)) { + if ( + this.hasRightChild(currentIndex) && + this.pairsIsInCorrectOrder( + this.rightChild(currentIndex), + this.leftChild(currentIndex) + ) + ) { + nextIndex = this.getRightChildIndex(currentIndex) + } else { + nextIndex = this.getLeftChildIndex(currentIndex) + } + + if ( + this.pairsIsInCorrectOrder( + this.heapContainer[currentIndex], + this.heapContainer[nextIndex] + ) + ) { + break + } + + this.swap(currentIndex, nextIndex) + currentIndex = nextIndex + } + } + + pairsIsInCorrectOrder(firstElement: T, secondElement: T): boolean { + throw new Error( + `You have to implement heap pair comparison method for ${firstElement} and ${secondElement} values` + ) + return false + } +} diff --git a/src/data-structures/heap/notes.md b/src/data-structures/heap/notes.md new file mode 100644 index 0000000..d81ea79 --- /dev/null +++ b/src/data-structures/heap/notes.md @@ -0,0 +1,19 @@ +## Heap + +> A specialized tree based data strcuture that satisfies the heap property + +* Efficiency - Heaps allow for efficent reterival and modification of the highest or lowest element, which is crucial for priority queues +* Ordering - Ensures well defined order for quick access to the priority element without the need to sort the entire dataset +* Memory usage - implemented using arrays, have a compact memory footprint +* Flexibility - dynamic, meaning they can grow and shrink as needed + + +E.g +- Priority Queues +- Sorting Algorithmns +- Graph Algorithms +- File Compression +- Dynamic Programming +- Medical Applications +- Load balancing +- Stock Market \ No newline at end of file From 4175de25f2c8ccfeca309b7d63991bfcf8902c7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A4ghib=20Hasan?= Date: Sat, 20 Apr 2024 20:58:10 +0300 Subject: [PATCH 2/2] WIP: add solution for MaxHeap --- src/data-structures/heap/max-heap.test.ts | 95 +++++++++++++++++++++++ src/data-structures/heap/max-heap.ts | 7 ++ 2 files changed, 102 insertions(+) create mode 100644 src/data-structures/heap/max-heap.test.ts create mode 100644 src/data-structures/heap/max-heap.ts diff --git a/src/data-structures/heap/max-heap.test.ts b/src/data-structures/heap/max-heap.test.ts new file mode 100644 index 0000000..8ebf5e8 --- /dev/null +++ b/src/data-structures/heap/max-heap.test.ts @@ -0,0 +1,95 @@ +import MaxHeap from './max-heap' + +describe('MaxHeap', () => { + it('should create an empty max heap', () => { + const maxHeap = new MaxHeap() + + expect(maxHeap).toBeDefined() + expect(maxHeap.peek()).toBeNull() + expect(maxHeap.isEmpty()).toBe(true) + }) + + it('should add items to the heap and heapify it up', () => { + const maxHeap = new MaxHeap() + + maxHeap.add(5) + expect(maxHeap.isEmpty()).toBe(false) + expect(maxHeap.peek()).toBe(5) + expect(maxHeap.toString()).toBe('5') + + maxHeap.add(3) + expect(maxHeap.peek()).toBe(5) + expect(maxHeap.toString()).toBe('5,3') + + maxHeap.add(2) + expect(maxHeap.peek()).toBe(5) + expect(maxHeap.toString()).toBe('5,3,2') + + maxHeap.add(10) + expect(maxHeap.peek()).toBe(10) + expect(maxHeap.toString()).toBe('10,5,2,3') + + maxHeap.add(1) + expect(maxHeap.peek()).toBe(10) + expect(maxHeap.toString()).toBe('10,5,2,3,1') + + maxHeap.add(1) + expect(maxHeap.peek()).toBe(10) + expect(maxHeap.toString()).toBe('10,5,2,3,1,1') + + expect(maxHeap.pool()).toBe(10) + expect(maxHeap.toString()).toBe('5,3,2,1,1') + + expect(maxHeap.pool()).toBe(5) + expect(maxHeap.toString()).toBe('3,1,2,1') + + expect(maxHeap.pool()).toBe(3) + expect(maxHeap.toString()).toBe('2,1,1') + }) + + it('should poll items from the heap and heapify it down', () => { + const maxHeap = new MaxHeap() + + maxHeap.add(5) + maxHeap.add(3) + maxHeap.add(10) + maxHeap.add(11) + maxHeap.add(1) + + expect(maxHeap.toString()).toBe('11,10,5,3,1') + + expect(maxHeap.pool()).toBe(11) + expect(maxHeap.toString()).toBe('10,3,5,1') + + expect(maxHeap.pool()).toBe(10) + expect(maxHeap.toString()).toBe('5,3,1') + + expect(maxHeap.pool()).toBe(5) + expect(maxHeap.toString()).toBe('3,1') + + expect(maxHeap.pool()).toBe(3) + expect(maxHeap.toString()).toBe('1') + + expect(maxHeap.pool()).toBe(1) + expect(maxHeap.toString()).toBe('') + + expect(maxHeap.pool()).toBeNull() + expect(maxHeap.toString()).toBe('') + }) + + it('should heapify down through the right branch as well', () => { + const maxHeap = new MaxHeap() + + maxHeap.add(3) + maxHeap.add(12) + maxHeap.add(10) + + expect(maxHeap.toString()).toBe('12,3,10') + + maxHeap.add(11) + expect(maxHeap.toString()).toBe('12,11,10,3') + + expect(maxHeap.pool()).toBe(12) + expect(maxHeap.toString()).toBe('11,3,10') + }) +}) diff --git a/src/data-structures/heap/max-heap.ts b/src/data-structures/heap/max-heap.ts new file mode 100644 index 0000000..ccbf2f6 --- /dev/null +++ b/src/data-structures/heap/max-heap.ts @@ -0,0 +1,7 @@ +import Heap from './heap' + +export default class MaxHeap extends Heap { + pairsIsInCorrectOrder(firstElement: T, secondElement: T) { + return this.compare.greaterThanOrEqual(firstElement, secondElement) + } +}