UNPKG

dastal

Version:

Data Structures & Algorithms implementations

133 lines 4.43 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.skewMerge = exports.sinkDown = exports.mergeKSorted = exports.heapify = exports.bubbleUp = void 0; /** * @internal */ function bubbleUp(index, compareFn, array) { const value = array[index]; // Until we reach the top of the heap while (index > 0) { // Get the parent const parentIndex = Math.floor((index + 1) / 2) - 1; const parent = array[parentIndex]; // If the parent is above or equal to value, the heap is in order if (compareFn(parent, value) <= 0) { break; } // Swap the parent with value and continue array[parentIndex] = value; array[index] = parent; index = parentIndex; } } exports.bubbleUp = bubbleUp; /** * @internal */ function heapify(compareFn, array) { for (let i = (array.length + 1) >>> 1; i > 0; sinkDown(--i, compareFn, array)) { } } exports.heapify = heapify; /** * @internal */ function mergeKSorted(compareFn, lists) { // Heapify the list of lists based on // the value at the head of each list. const compare = (a, b) => compareFn(a.value, b.value); heapify(compare, lists); // Combine the lists into a single list. const list = lists[0]; for (let tail = list; lists.length > 1; tail = tail.next) { lists[0] = lists[0].next ?? lists.pop(); sinkDown(0, compare, lists); tail.next = lists[0]; } return list; } exports.mergeKSorted = mergeKSorted; /** * @internal */ function sinkDown(index, compareFn, array) { const n = array.length; const value = array[index]; do { // Compute the left child's index let childIndex = 2 * index + 1; // If no children exist if (childIndex >= n) { break; } // Decide which child to compare with let child = array[childIndex]; if (childIndex + 1 < n && compareFn(array[childIndex + 1], child) <= 0) { child = array[++childIndex]; } // If value <= child if (compareFn(value, child) <= 0) { break; } // Swap value and child array[index] = child; array[childIndex] = value; index = childIndex; } while (true); } exports.sinkDown = sinkDown; /** * See: https://en.wikipedia.org/wiki/Skew_heap#Merging_two_heaps * * @param compareFn - A function used to determine the order of the heap. * * It is expected to return: * - A negative value if first argument < second argument * - Zero if first argument == second argument * - A positive value if first argument > second argument * @param heaps - An iterable of heaps to merge * * @returns The new heap */ function skewMerge(compareFn, heaps) { // Remove undefineds and initialize a list for each heap const lists = []; for (let i = 0; i < heaps.length; ++i) { if (heaps[i] != null) { lists.push({ value: heaps[i] }); } } // Check if nothing to merge with if (lists.length < 2) { return lists[0]?.value; } // Split each heap into subheaps by cutting every right path; From the root // node, sever the right node to make the right child its own heap. Repeat // until you can't go right. This will turn each heap into a list of heaps // where the root either only has a left child or no children at all. The // lists of heaps will be in desc order (from bottom to top). for (let i = 0; i < lists.length; ++i) { let list = lists[i]; let tree = list.value; while ((tree = tree.right)) { list = { next: list, value: tree }; } lists[i] = list; } // Combine the lists into a single list in desc order let list = mergeKSorted((a, b) => compareFn(b.value, a.value), lists); // While there are still multiple heaps, iteratively combine // the first two (from left to right). If the root of the second-to-first // subtree has a left child, swap it to be the right child. Link the root // of the last subtree as the left child of the second-to-first subtree. let heap = list.value; while ((list = list.next)) { const node = list.value; node.right = node.left; node.left = heap; heap = node; } return heap; } exports.skewMerge = skewMerge; //# sourceMappingURL=utils.js.map