implementing a minmaxheap

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:

heap.js
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!