UNPKG

atomic-fns

Version:

Like Lodash, but for ESNext and with types. Stop shipping code built for browsers from 2015.

182 lines (181 loc) 5.17 kB
import { compare } from '../operators/index.js'; import { Collection } from './abc.js'; export class Heap extends Collection { heap; compare; count = 0; /** * Initializes a new Heap instance. * * **Note:** When constructing the heap from an array, it will operate directly on this array. For other iterables, it will create a new array. * * @param {Iterable<T>} [container=[]] The initial values. * @param {Comparer} [cmp=compare] Compare function. Defaults to smaller values first. */ constructor(container = [], cmp = compare) { super(); this.compare = cmp; if (Array.isArray(container)) { // use the provided array to avoid copying. this.heap = container; } else { this.heap = Array.from(container); } this.count = this.heap.length; const halfLength = this.count >> 1; // Heapify the items for (let parent = (this.count - 1) >> 1; parent >= 0; --parent) { heapifyDown(this.heap, parent, halfLength, this.compare); } } at(n) { return this.heap.at(n); } get size() { return this.count; } clear() { this.count = 0; this.heap.length = 0; } /** * Push element into a container in order. * @param item The element to push. */ add(item) { this.heap.push(item); heapifyUp(this.heap, this.count, this.compare); this.count += 1; } /** * Removes the top element. */ pop() { if (!this.count) return; const value = this.heap[0]; const last = this.heap.pop(); this.count -= 1; if (this.count) { this.heap[0] = last; heapifyDown(this.heap, 0, this.count >> 1, this.compare); } return value; } /** * Accesses the top element. */ top() { if (!this.count) return; return this.heap[0]; } /** * Check if element is in heap. * @param item The item want to find. * @return `true` if element exists. */ contains(item) { if (!this.count) return false; return this.heap.includes(item); } /** * Remove specified item from heap. * @param item The item want to remove. * @return `true` if the item was removed. */ remove(item) { const index = this.heap.indexOf(item); if (index < 0) return false; if (index === 0) { this.pop(); } else if (index === this.count - 1) { this.heap.pop(); this.count -= 1; } else { this.heap.splice(index, 1, this.heap.pop()); this.count -= 1; heapifyUp(this.heap, index, this.compare); heapifyDown(this.heap, index, this.count >> 1, this.compare); } return true; } /** * Returns an iterable with all the values in the heap. * @returns {Iterable<T>} The values in the heap. */ values() { return this.heap[Symbol.iterator](); } } function heapifyDown(heap, i, halfLength, compareFn = compare) { const item = heap[i]; while (i < halfLength) { let left = (i << 1) | 1; const right = left + 1; let minItem = heap[left]; if (right < heap.length && compareFn(minItem, heap[right]) > 0) { left = right; minItem = heap[right]; } if (compareFn(minItem, item) >= 0) break; heap[i] = minItem; i = left; } heap[i] = item; } function heapifyUp(heap, i, compareFn) { const item = heap[i]; while (i > 0) { const parent = (i - 1) >> 1; const parentItem = heap[parent]; if (compareFn(parentItem, item) <= 0) break; heap[i] = parentItem; i = parent; } heap[i] = item; } /** * Transform any array into a heap, in-place, in linear time. * @param {Array} heap * @param {Comparer} [compareFn=compare] Custom compare function */ export function heapify(heap, compareFn = compare) { const size = heap.length; const halfLength = size >> 1; for (let parent = (size - 1) >> 1; parent >= 0; --parent) { heapifyDown(heap, parent, halfLength, compareFn); } } /** * Push the value `item` onto the `heap`, maintaining the heap invariant. * @param {Array} heap * @param {*} item * @param {Comparer} [compareFn=compare] Custom compare function */ export function heappush(heap, item, compareFn = compare) { heap.push(item); heapifyUp(heap, heap.length - 1, compareFn); } /** * Pop and return the smallest item from the `heap`, maintaining the heap invariant. * @param {Array} heap * @param {Comparer} [compareFn=compare] Custom compare function */ export function heappop(heap, compareFn = compare) { const last = heap.pop(); if (heap.length) { const item = heap[0]; heap[0] = last; heapifyDown(heap, 0, heap.length >> 1, compareFn); return item; } return last; }