Skip to content

Instantly share code, notes, and snippets.

@dsasse07
Last active June 22, 2021 19:48
Show Gist options
  • Save dsasse07/7795bf6255f9ef518998fab604b854aa to your computer and use it in GitHub Desktop.
Save dsasse07/7795bf6255f9ef518998fab604b854aa to your computer and use it in GitHub Desktop.
MaxHeap with heapify & Heap sort
class MaxHeap {
constructor(arr = []){
this.size = 0
this.values = this._heapify(arr)
}
insert(value){
// If no value, do nothing
if (value === undefined) return
// Insert the value, and increment the size of the heap
this.values.push(value)
this.size++
// Check to see if there is not more than 1 item in the heap
// If there is only 1 item, there is no need to bubble up
if (this.size > 1) this._bubbleUp()
return this.values
}
extract(){
if (this.size === 0) return
const lastIndex = this.size - 1
this.size--
this._swap(0, lastIndex)
const extractValue = this.values.pop()
if (this.size > 1) this._trickleDown()
return extractValue
}
// Helper function using object destructuring to swap the elements at two indices
_swap(index1, index2){
[this.values[index1], this.values[index2]] = [this.values[index2], this.values[index1]]
}
_heapify(arr){
if (this.size > 0) return // Optional: Prevent overriding existing heap values
this.size = arr.length
/**
* To prevent mutating current array, replace line 18 with
* this.values = [...arr]
*/
this.values = arr
const nodeCount = this.size - 1
// Finds the last node of the tree that has children
let cIdx = Math.floor((nodeCount - 2)/2)
/** For each node up through the root,
* call trickleDown
*/
for (let i = cIdx; i >= 0; i--){
this._trickleDown(i)
}
return this.values
}
_bubbleUp(){
// Grab the most recently added value and its parent
let currentIndex = this.size - 1
let parentIndex = Math.floor( (currentIndex - 1) / 2 )
// Swap the new node with its parent until the new node either
// becomes the root, or is no longer greater than its parent
while (parentIndex >= 0 && this.values[currentIndex] > this.values[parentIndex]){
this._swap(currentIndex, parentIndex)
currentIndex = parentIndex
parentIndex = Math.floor((currentIndex - 1) / 2 )
}
}
_trickleDown(start = 0, stop = this.size){
let currentIndex = start
/**
* These will be the indexes corresponding to the left and right
* child of the node at currentIndex
* swapIdx will be which of the children the currentIndex will
* actually switch with, if any
*/
let leftIdx, rightIdx, swapIdx
while (true) {
leftIdx = 2 * currentIndex + 1
rightIdx = 2 * currentIndex + 2
swapIdx = null
/**
* If there is a valid left child and it is greater than the current value,
* prepare to swap it
*/
if (
leftIdx < stop &&
this.values[currentIndex] < this.values[leftIdx]
) {
swapIdx = leftIdx
}
/**
* If there is a valid right child and it is greater than the current value,
* prepare to swap it if we haven't already prepared to swap with left child.
* If we have prepared to swap with left child, we should only choose to swapIdx
* with the right child instead if it is greater than the left child, meaning
* it better fits the heap rule
*/
if (
rightIdx < stop &&
((swapIdx === null &&
this.values[currentIndex] < this.values[rightIdx]) ||
(swapIdx !== null && this.values[rightIdx] > this.values[leftIdx]))
) {
swapIdx = rightIdx
}
if (swapIdx === null) break // If no possibel swap was ID'd, we're done
// Swap the parent with the identified child, update the currentIndex, and repeat
this._swap(currentIndex, swapIdx)
currentIndex = swapIdx
}
}
static heapSort(arr){
const heap = new MaxHeap(arr)
for (let i = arr.length - 1; i > 0; i--){
heap._swap(0,i)
heap._trickleDown(0, i)
}
return heap.values
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment