2022-01-09
|~3 min read
|413 words
Many moons ago, I had an interview where I was asked to find the x smallest value in a list.
Javascript makes that fairly easy, however, my interviewer really wanted me to use a heap. I didn’t know how heaps worked at the time, so after I failed the interview, I went home to learn.
Here’s my implementation:
function BinaryHeap(style) {
this._heap = []
this._compare = function (i, j) {
if (style === "MAX") {
return !!(i > j)
} else {
return !!(i < j)
}
}
}
BinaryHeap.prototype.print = function () {
console.log(this._heap)
}
BinaryHeap.prototype.getRoot = function () {
return this._heap[0]
}
BinaryHeap.prototype._swap = function (index, parentIndex) {
const placeHolder = this._heap[index]
this._heap[index] = this._heap[parentIndex]
this._heap[parentIndex] = placeHolder
}
BinaryHeap.prototype._getParentIndex = function (index) {
return Math.floor((index - 1) / 2)
}
/**
* Selects a child based on the style of heap - if min heap, will pick the smaller. if max heap will pick the larger.
* Can return undefined if the children are _not_ within the heap - this is the purpose of the filter
*/
BinaryHeap.prototype._getChildIndex = function (parentIndex) {
const childrenIndices = [parentIndex * 2 + 1, parentIndex * 2 + 2].filter(
(index) => index < this._heap.length,
)
const firstChild = this._heap[childrenIndices[0]]
const secondChild = this._heap[childrenIndices[1]]
if (this._compare(firstChild, secondChild)) {
return childrenIndices[0]
} else {
return childrenIndices[1]
}
}
BinaryHeap.prototype.insert = function (value) {
this._heap.push(value)
let currentIndex = this._heap.length - 1
let parentIndex = this._getParentIndex(currentIndex)
let parentValue = this._heap[parentIndex]
let childValue = this._heap[currentIndex]
while (currentIndex && this._compare(childValue, parentValue)) {
this._swap(currentIndex, parentIndex)
currentIndex = parentIndex
parentIndex = this._getParentIndex(currentIndex)
parentValue = this._heap[parentIndex]
childValue = this._heap[currentIndex]
}
}
BinaryHeap.prototype.removeRoot = function () {
this._swap(this._heap.length - 1, 0)
const rootValue = this._heap.pop()
let parentIndex = 0
let childIndex = this._getChildIndex(parentIndex)
let parentValue = this._heap[parentIndex]
let childValue = this._heap[childIndex]
while (childIndex && this._compare(childValue, parentValue)) {
this._swap(childIndex, parentIndex)
parentIndex = childIndex
childIndex = this._getChildIndex(parentIndex)
parentValue = this._heap[parentIndex]
childValue = this._heap[childIndex]
}
return rootValue
}
BinaryHeap.prototype._sort = function (childIndex, parentIndex) {
let parentValue = this._heap[parentIndex]
let childValue = this._heap[childIndex]
while (childIndex && this._compare(childValue, parentValue)) {
this._swap(childIndex, parentIndex)
parentIndex = childIndex
childIndex = this._getChildIndex(parentIndex)
parentValue = this._heap[parentIndex]
childValue = this._heap[childIndex]
}
}
module.exports = { BinaryHeap }
There area also libraries for heaps, but I find that only by writing my own do I really understand, so that’s what this is.
One of the notable design decisions included in my version of a heap is that I resort the heap on every insert.
Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!