Binary Heap

P
P
D
H
/**
 * BinaryHeap class represents a binary heap data structure that can be configured as a Min Heap or Max Heap.
 *
 * Binary heaps are binary trees that are filled level by level and from left to right inside each level.
 * They have the property that any parent node has a smaller (for Min Heap) or greater (for Max Heap) priority
 * than its children, ensuring that the root of the tree always holds the extremal value.
 */
class BinaryHeap {
  /**
   * Creates a new BinaryHeap instance.
   * @constructor
   * @param {Function} comparatorFunction - The comparator function used to determine the order of elements (e.g., minHeapComparator or maxHeapComparator).
   */
  constructor(comparatorFunction) {
    /**
     * The heap array that stores elements.
     * @member {Array}
     */
    this.heap = []

    /**
     * The comparator function used for ordering elements in the heap.
     * @member {Function}
     */
    this.comparator = comparatorFunction
  }

  /**
   * Inserts a new value into the heap.
   * @param {*} value - The value to be inserted into the heap.
   */
  insert(value) {
    this.heap.push(value)
    this.#bubbleUp(this.heap.length - 1)
  }

  /**
   * Returns the number of elements in the heap.
   * @returns {number} - The number of elements in the heap.
   */
  size() {
    return this.heap.length
  }

  /**
   * Checks if the heap is empty.
   * @returns {boolean} - True if the heap is empty, false otherwise.
   */
  empty() {
    return this.size() === 0
  }

  /**
   * Bubbles up a value from the specified index to maintain the heap property.
   * @param {number} currIdx - The index of the value to be bubbled up.
   * @private
   */
  #bubbleUp(currIdx) {
    let parentIdx = Math.floor((currIdx - 1) / 2)

    while (
      currIdx > 0 &&
      this.comparator(this.heap[currIdx], this.heap[parentIdx])
    ) {
      this.#swap(currIdx, parentIdx)
      currIdx = parentIdx
      parentIdx = Math.floor((currIdx - 1) / 2)
    }
  }

  /**
   * Sinks down a value from the specified index to maintain the heap property.
   * @param {number} currIdx - The index of the value to be sunk down.
   * @private
   */
  #sinkDown(currIdx) {
    let childOneIdx = currIdx * 2 + 1

    while (childOneIdx < this.size()) {
      const childTwoIdx = childOneIdx + 1 < this.size() ? childOneIdx + 1 : -1
      const swapIdx =
        childTwoIdx !== -1 &&
        this.comparator(this.heap[childTwoIdx], this.heap[childOneIdx])
          ? childTwoIdx
          : childOneIdx

      if (this.comparator(this.heap[swapIdx], this.heap[currIdx])) {
        this.#swap(currIdx, swapIdx)
        currIdx = swapIdx
        childOneIdx = currIdx * 2 + 1
      } else {
        return
      }
    }
  }

  /**
   * Retrieves the top element of the heap without removing it.
   * @returns {*} - The top element of the heap.
   */
  peek() {
    return this.heap[0]
  }

  /**
   * Removes and returns the top element of the heap.
   * @returns {*} - The top element of the heap.
   */
  extractTop() {
    const top = this.peek()
    const last = this.heap.pop()

    if (!this.empty()) {
      this.heap[0] = last
      this.#sinkDown(0)
    }

    return top
  }

  /**
   * Swaps elements at two specified indices in the heap.
   * @param {number} index1 - The index of the first element to be swapped.
   * @param {number} index2 - The index of the second element to be swapped.
   * @private
   */
  #swap(index1, index2) {
    ;[this.heap[index1], this.heap[index2]] = [
      this.heap[index2],
      this.heap[index1]
    ]
  }
}

/**
 * Comparator function for creating a Min Heap.
 * @param {*} a - The first element to compare.
 * @param {*} b - The second element to compare.
 * @returns {boolean} - True if 'a' should have higher priority than 'b' in the Min Heap, false otherwise.
 */
const minHeapComparator = (a, b) => a < b

/**
 * Comparator function for creating a Max Heap.
 * @param {*} a - The first element to compare.
 * @param {*} b - The second element to compare.
 * @returns {boolean} - True if 'a' should have higher priority than 'b' in the Max Heap, false otherwise.
 */
const maxHeapComparator = (a, b) => a > b

export { BinaryHeap, minHeapComparator, maxHeapComparator }