Skip to content
Draft
Show file tree
Hide file tree
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
3 changes: 2 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@

--

## Data-Structures (5 / -)
## Data-Structures (6 / -)

| Name | Solution |
| ------------------ | ------------------------------------------------------ |
Expand All @@ -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

Expand Down
12 changes: 12 additions & 0 deletions src/data-structures/heap/heap.test.ts
Original file line number Diff line number Diff line change
@@ -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()
})
})
197 changes: 197 additions & 0 deletions src/data-structures/heap/heap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import Comparator from '../utils/comparator'

export default class Heap<T> {
public heapContainer: T[]
public compare: Comparator<T>

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
}
}
95 changes: 95 additions & 0 deletions src/data-structures/heap/max-heap.test.ts
Original file line number Diff line number Diff line change
@@ -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')
})
})
7 changes: 7 additions & 0 deletions src/data-structures/heap/max-heap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Heap from './heap'

export default class MaxHeap<T> extends Heap<T> {
pairsIsInCorrectOrder(firstElement: T, secondElement: T) {
return this.compare.greaterThanOrEqual(firstElement, secondElement)
}
}
19 changes: 19 additions & 0 deletions src/data-structures/heap/notes.md
Original file line number Diff line number Diff line change
@@ -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