Mnemonist

Heap


A Heap is basically a priority queue.

For more information about the Heap, you can head here.

By default, the provided Heap is a min heap and the MaxHeap is just some sugar that will reverse the provided comparator for you.

const Heap = require('mnemonist/heap');

// To access min/max heap
const { MinHeap } = require('mnemonist/heap');
const { MaxHeap } = require('mnemonist/heap');

If you know the maximum number of items you will store and need a more performant implementation, check out the FixedReverseHeap.

Use case

Let’s say we need to schedule tasks having different priorities. A Stack or a Queue might not be enough since we need to be sure at all time that the most urgent tasks will come first.

In such cases, a Heap can help you tremendously by keeping the items you give it ordered following arbitrary logic & preventing you from having to perform costly sort operations on the full list of tasks.

// Let's give a comparator function to our Heap
// to be able to tell which tasks are the most urgent
const heap = new Heap((a, b) => {
  if (a.priority > b.priority)
    return -1;
  if (a.priority < b.priority)
    return 1;
  return 0;
});

// Let's add some tasks
heap.push({priority: 12, task: 'clean'});
heap.push({priority: 2, task: 'sleep'});
heap.push({priority: 23, task: 'work'});

// Let's peek to see which is the most urgent task
heap.peek().task;
>>> 'work'

// Let's perform our tasks
while (heap.size) {
  const task = heap.pop().task;
  console.log('Doing:', task);
}

Constructor

The Heap takes a single optional argument being the comparator function to be used to compare the given items.

// Providing a comparator to handle custom objects
const heap = new Heap((a, b) => {
  if (a.value < b.value)
    return -1;
  if (a.value > b.value)
    return 1;
  return 0;
});

heap.push({value: 34});
heap.push({value: 45});

heap.peek();
>>> 34

Static #.from

Alternatively, one can build a Heap from an arbitrary JavaScript iterable likewise:

const heap = Heap.from([1, 2, 3], comparator);

The construction is done in linear time.

Members

Methods

Mutation

Read

Helpers

If you don’t want to use the Heap class and want to rely on your own array to represent the heap’s data, you can use the helpers below:

If you need to find the n smallest/largest elements from arbitrary iterables efficiently you can use those helpers using heaps under the hood:

#.size

Number of items in the heap.

const heap = new Heap();
heap.size
>>> 0

#.push

Pushes an item into the heap.

O(log n)

const heap = new Heap();
heap.push(34);

#.pop

Retrieve & remove the min item of the heap (or the max item in case of a MaxHeap).

O(log n)

const heap = new Heap();

heap.push(4);
heap.push(34);
heap.push(5);

heap.pop();
>>> 4

heap.size
>>> 2

#.replace

Pop the heap, push an item then return the popped value. It will throw if the heap is empty.

Note that the returned value may be smaller/larger than the given item so be sure to wrap its usage within reasonable conditions!

This is more efficient than doing pop then push and does not change the length of the underlying array.

O(log n)

const heap = new Heap();

heap.push(1);
heap.replace(2);
>>> 1

heap.size
>>> 1

heap.pop();
>>> 2

#.pushpop

Push an item into the heap, then pop the heap. If the heap is empty, the heap will obviously remain empty and you will get your item back.

This is more efficient than doing push than pop and does not change the length of the underlying array.

O(log n)

const heap = new Heap();

heap.push(1);
heap.pushpop(2);
>>> 1

heap.size
>>> 1

heap.pop();
>>> 2

#.consume

Fully consume the heap and return its items as a sorted array.

const heap = new Heap();

heap.push(45);
heap.push(-3);
heap.push(0);

heap.consume();
>>> [-3, 0, 45]

heap.size
>>> 0

#.clear

Completely clears the heap.

const heap = new Heap();

heap.push(34);
heap.clear();
heap.size
>>> 0

#.peek

Retrieves the min item of the heap (or the max item in case of a MaxHeap).

O(1)

const heap = new Heap();

heap.push(4);
heap.push(34);
heap.push(5);

heap.peek();
>>> 4

#.toArray

Converts the heap into an array without altering the heap’s state. Note that the underlying array storing the items is cloned to perform this operation and that you can be more performant if you can consume the heap instead.

This method is mostly used for debugging purposes.

O(n log n)

const heap = new Heap();

heap.push(4);
heap.push(34);
heap.push(5);

heap.toArray();
>>> [4, 5, 34]



heapify

Converts the given array into a heap in linear time.

O(n)

const array = [4, 1, -5, 10];

Heap.heapify(comparator, array);
// You array has been heapified!

push

Push a new item into the heap.

O(log n)

const array = [];

Heap.push(comparator, array, 4);
Heap.push(comparator, array, 3);
Heap.push(comparator, array, 7);

array[0]
>>> 3

pop

Pop the heap.

O(log n)

const array = [];

Heap.push(comparator, array, 4);
Heap.push(comparator, array, 3);
Heap.push(comparator, array, 7);

Heap.pop(comparator, array);
>>> 3

replace

Pop the heap, push an item then return the popped value. It will throw if the heap is empty.

Note that the returned value may be smaller/larger than the given item so be sure to wrap its usage within reasonable conditions!

This is more efficient than doing pop then push and does not change the length of the underlying array.

O(log n)

const array = [];

Heap.push(comparator, array, 1);
Heap.replace(comparator, array, 2);
>>> 1

array.length
>>> 1

Heap.pop(comparator, array);
>>> 2

pushpop

Push an item into the heap, then pop the heap. If the heap is empty, the heap will obviously remain empty and you will get your item back.

This is more efficient than doing push than pop and does not change the length of the underlying array.

O(log n)

const array = [];

Heap.push(comparator, array, 1);
Heap.pushpop(comparator, array, 2);
>>> 1

array.length
>>> 1

Heap.pop(comparator, array);
>>> 2

consume

Completely consumes the heap and returns all its items in a sorted array.

O(n log n)

const array = [4, 1, 3];

Heap.heapify(comparator, array);
Heap.consume(comparator, array);
>>> [1, 3, 4]

array.length
>>> 0



nsmallest

Retrieves the n smallest values from the given iterables.

Runs in O(N log n), N being the total number of values, and n the number of smallest values you want.

Takes up to O(n) space, n being the number of smallest values you want.

Heap.nsmallest(3, [4, 2, 1, 5, 6, 8]);
>>> [1, 2, 4]

// If you need a custom comparator
Heap.nsmallest(comparator, 3, [4, 2, 1, 5, 6, 8]);

nlargest

Retrieves the n largest values from the given iterables.

Runs in O(N log n), N being the total number of values, and n the number of largest values you want.

Takes up to O(n) space, n being the number of largest values you want.

Heap.nlargest(3, [4, 2, 1, 5, 6, 8]);
>>> [8, 6, 5]

// If you need a custom comparator
// BEWARE: must be the same as you would use with nsmallest!
Heap.nlargest(comparator, 3, [4, 2, 1, 5, 6, 8]);