doubly-linked-list-typed
Version:
Doubly Linked List
911 lines (910 loc) • 32.5 kB
JavaScript
/**
* data-structure-typed
* @author Kirk Qi
* @copyright Copyright (c) 2022 Kirk Qi <qilinaus@gmail.com>
* @license MIT License
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.FibonacciHeap = exports.FibonacciHeapNode = exports.Heap = void 0;
const base_1 = require("../base");
/**
* 1. Complete Binary Tree: Heaps are typically complete binary trees, meaning every level is fully filled except possibly for the last level, which has nodes as far left as possible.
* 2. Heap Properties: Each node in a heap follows a specific order property, which varies depending on the type of heap:
* Max Heap: The value of each parent node is greater than or equal to the value of its children.
* Min Heap: The value of each parent node is less than or equal to the value of its children.
* 3. Root Node Access: In a heap, the largest element (in a max heap) or the smallest element (in a min heap) is always at the root of the tree.
* 4. Efficient Insertion and Deletion: Due to its structure, a heap allows for insertion and deletion operations in logarithmic time (O(log n)).
* 5. Managing Dynamic Data Sets: Heaps effectively manage dynamic data sets, especially when frequent access to the largest or smallest elements is required.
* 6. Non-linear Search: While a heap allows rapid access to its largest or smallest element, it is less efficient for other operations, such as searching for a specific element, as it is not designed for these tasks.
* 7. Efficient Sorting Algorithms: For example, heap sort. Heap sort uses the properties of a heap to sort elements.
* 8. Graph Algorithms: Such as Dijkstra's shortest path algorithm and Prime's minimum-spanning tree algorithm, which use heaps to improve performance.
* @example
* // Use Heap to sort an array
* function heapSort(arr: number[]): number[] {
* const heap = new Heap<number>(arr, { comparator: (a, b) => a - b });
* const sorted: number[] = [];
* while (!heap.isEmpty()) {
* sorted.push(heap.poll()!); // Poll minimum element
* }
* return sorted;
* }
*
* const array = [5, 3, 8, 4, 1, 2];
* console.log(heapSort(array)); // [1, 2, 3, 4, 5, 8]
* @example
* // Use Heap to solve top k problems
* function topKElements(arr: number[], k: number): number[] {
* const heap = new Heap<number>([], { comparator: (a, b) => b - a }); // Max heap
* arr.forEach(num => {
* heap.add(num);
* if (heap.size > k) heap.poll(); // Keep the heap size at K
* });
* return heap.toArray();
* }
*
* const numbers = [10, 30, 20, 5, 15, 25];
* console.log(topKElements(numbers, 3)); // [15, 10, 5]
* @example
* // Use Heap to merge sorted sequences
* function mergeSortedSequences(sequences: number[][]): number[] {
* const heap = new Heap<{ value: number; seqIndex: number; itemIndex: number }>([], {
* comparator: (a, b) => a.value - b.value // Min heap
* });
*
* // Initialize heap
* sequences.forEach((seq, seqIndex) => {
* if (seq.length) {
* heap.add({ value: seq[0], seqIndex, itemIndex: 0 });
* }
* });
*
* const merged: number[] = [];
* while (!heap.isEmpty()) {
* const { value, seqIndex, itemIndex } = heap.poll()!;
* merged.push(value);
*
* if (itemIndex + 1 < sequences[seqIndex].length) {
* heap.add({
* value: sequences[seqIndex][itemIndex + 1],
* seqIndex,
* itemIndex: itemIndex + 1
* });
* }
* }
*
* return merged;
* }
*
* const sequences = [
* [1, 4, 7],
* [2, 5, 8],
* [3, 6, 9]
* ];
* console.log(mergeSortedSequences(sequences)); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
* @example
* // Use Heap to dynamically maintain the median
* class MedianFinder {
* private low: MaxHeap<number>; // Max heap, stores the smaller half
* private high: MinHeap<number>; // Min heap, stores the larger half
*
* constructor() {
* this.low = new MaxHeap<number>([]);
* this.high = new MinHeap<number>([]);
* }
*
* addNum(num: number): void {
* if (this.low.isEmpty() || num <= this.low.peek()!) this.low.add(num);
* else this.high.add(num);
*
* // Balance heaps
* if (this.low.size > this.high.size + 1) this.high.add(this.low.poll()!);
* else if (this.high.size > this.low.size) this.low.add(this.high.poll()!);
* }
*
* findMedian(): number {
* if (this.low.size === this.high.size) return (this.low.peek()! + this.high.peek()!) / 2;
* return this.low.peek()!;
* }
* }
*
* const medianFinder = new MedianFinder();
* medianFinder.addNum(10);
* console.log(medianFinder.findMedian()); // 10
* medianFinder.addNum(20);
* console.log(medianFinder.findMedian()); // 15
* medianFinder.addNum(30);
* console.log(medianFinder.findMedian()); // 20
* medianFinder.addNum(40);
* console.log(medianFinder.findMedian()); // 25
* medianFinder.addNum(50);
* console.log(medianFinder.findMedian()); // 30
* @example
* // Use Heap for load balancing
* function loadBalance(requests: number[], servers: number): number[] {
* const serverHeap = new Heap<{ id: number; load: number }>([], { comparator: (a, b) => a.load - b.load }); // min heap
* const serverLoads = new Array(servers).fill(0);
*
* for (let i = 0; i < servers; i++) {
* serverHeap.add({ id: i, load: 0 });
* }
*
* requests.forEach(req => {
* const server = serverHeap.poll()!;
* serverLoads[server.id] += req;
* server.load += req;
* serverHeap.add(server); // The server after updating the load is re-entered into the heap
* });
*
* return serverLoads;
* }
*
* const requests = [5, 2, 8, 3, 7];
* console.log(loadBalance(requests, 3)); // [12, 8, 5]
* @example
* // Use Heap to schedule tasks
* type Task = [string, number];
*
* function scheduleTasks(tasks: Task[], machines: number): Map<number, Task[]> {
* const machineHeap = new Heap<{ id: number; load: number }>([], { comparator: (a, b) => a.load - b.load }); // Min heap
* const allocation = new Map<number, Task[]>();
*
* // Initialize the load on each machine
* for (let i = 0; i < machines; i++) {
* machineHeap.add({ id: i, load: 0 });
* allocation.set(i, []);
* }
*
* // Assign tasks
* tasks.forEach(([task, load]) => {
* const machine = machineHeap.poll()!;
* allocation.get(machine.id)!.push([task, load]);
* machine.load += load;
* machineHeap.add(machine); // The machine after updating the load is re-entered into the heap
* });
*
* return allocation;
* }
*
* const tasks: Task[] = [
* ['Task1', 3],
* ['Task2', 1],
* ['Task3', 2],
* ['Task4', 5],
* ['Task5', 4]
* ];
* const expectedMap = new Map<number, Task[]>();
* expectedMap.set(0, [
* ['Task1', 3],
* ['Task4', 5]
* ]);
* expectedMap.set(1, [
* ['Task2', 1],
* ['Task3', 2],
* ['Task5', 4]
* ]);
* console.log(scheduleTasks(tasks, 2)); // expectedMap
*/
class Heap extends base_1.IterableElementBase {
/**
* The constructor initializes a heap data structure with optional elements and options.
* @param elements - The `elements` parameter is an iterable object that contains the initial
* elements to be added to the heap.
* It is an optional parameter, and if not provided, the heap will
* be initialized as empty.
* @param [options] - The `options` parameter is an optional object that can contain additional
* configuration options for the heap.
* In this case, it is used to specify a custom comparator
* function for comparing elements in the heap.
* The comparator function is used to determine the
* order of elements in the heap.
*/
constructor(elements = [], options) {
super(options);
this._elements = [];
this._DEFAULT_COMPARATOR = (a, b) => {
if (typeof a === 'object' || typeof b === 'object') {
throw TypeError(`When comparing object types, a custom comparator must be defined in the constructor's options parameter.`);
}
if (a > b)
return 1;
if (a < b)
return -1;
return 0;
};
this._comparator = this._DEFAULT_COMPARATOR;
if (options) {
const { comparator } = options;
if (comparator)
this._comparator = comparator;
}
this.addMany(elements);
}
/**
* The function returns an array of elements.
* @returns The element array is being returned.
*/
get elements() {
return this._elements;
}
/**
* Get the size (number of elements) of the heap.
*/
get size() {
return this.elements.length;
}
/**
* Get the last element in the heap, which is not necessarily a leaf node.
* @returns The last element or undefined if the heap is empty.
*/
get leaf() {
var _a;
return (_a = this.elements[this.size - 1]) !== null && _a !== void 0 ? _a : undefined;
}
/**
* Static method that creates a binary heap from an array of elements and a comparison function.
* @returns A new Heap instance.
* @param elements
* @param options
*/
static heapify(elements, options) {
return new Heap(elements, options);
}
/**
* Time Complexity: O(log n)
* Space Complexity: O(1)
*
* The add function pushes an element into an array and then triggers a bubble-up operation.
* @param {E} element - The `element` parameter represents the element that you want to add to the
* data structure.
* @returns The `add` method is returning a boolean value, which is the result of calling the
* `_bubbleUp` method with the index `this.elements.length - 1` as an argument.
*/
add(element) {
this._elements.push(element);
return this._bubbleUp(this.elements.length - 1);
}
/**
* Time Complexity: O(k log n)
* Space Complexity: O(1)
*
* The `addMany` function iterates over elements and adds them to a collection, returning an array of
* boolean values indicating success or failure.
* @param {Iterable<E> | Iterable<R>} elements - The `elements` parameter in the `addMany` method is
* an iterable containing elements of type `E` or `R`. The method iterates over each element in the
* iterable and adds them to the data structure. If a transformation function `_toElementFn` is
* provided, it transforms the element
* @returns The `addMany` method returns an array of boolean values indicating whether each element
* in the input iterable was successfully added to the data structure.
*/
addMany(elements) {
const ans = [];
for (const el of elements) {
if (this._toElementFn) {
ans.push(this.add(this._toElementFn(el)));
continue;
}
ans.push(this.add(el));
}
return ans;
}
/**
* Time Complexity: O(log n)
* Space Complexity: O(1)
*
* Remove and return the top element (the smallest or largest element) from the heap.
* @returns The top element or undefined if the heap is empty.
*/
poll() {
if (this.elements.length === 0)
return;
const value = this.elements[0];
const last = this.elements.pop();
if (this.elements.length) {
this.elements[0] = last;
this._sinkDown(0, this.elements.length >> 1);
}
return value;
}
/**
* Time Complexity: O(1)
* Space Complexity: O(1)
*
* Peek at the top element of the heap without removing it.
* @returns The top element or undefined if the heap is empty.
*/
peek() {
return this.elements[0];
}
/**
* Check if the heap is empty.
* @returns True if the heap is empty, otherwise false.
*/
isEmpty() {
return this.size === 0;
}
/**
* Reset the elements of the heap. Make the elements empty.
*/
clear() {
this._elements = [];
}
/**
* Time Complexity: O(n)
* Space Complexity: O(n)
*
* Clear and add elements of the heap
* @param elements
*/
refill(elements) {
this._elements = elements;
return this.fix();
}
/**
* Time Complexity: O(n)
* Space Complexity: O(1)
*
* Use a comparison function to check whether a binary heap contains a specific element.
* @param element - the element to check.
* @returns Returns true if the specified element is contained; otherwise, returns false.
*/
has(element) {
return this.elements.includes(element);
}
/**
* Time Complexity: O(n)
* Space Complexity: O(1)
*
* The `delete` function removes an element from an array-like data structure, maintaining the order
* and structure of the remaining elements.
* @param {E} element - The `element` parameter represents the element that you want to delete from
* the array `this.elements`.
* @returns The `delete` function is returning a boolean value. It returns `true` if the element was
* successfully deleted from the array, and `false` if the element was not found in the array.
*/
delete(element) {
const index = this.elements.indexOf(element);
if (index < 0)
return false;
if (index === 0) {
this.poll();
}
else if (index === this.elements.length - 1) {
this.elements.pop();
}
else {
this.elements.splice(index, 1, this.elements.pop());
this._bubbleUp(index);
this._sinkDown(index, this.elements.length >> 1);
}
return true;
}
/**
* Time Complexity: O(n)
* Space Complexity: O(log n)
*
* Depth-first search (DFS) method, different traversal orders can be selected。
* @param order - Traverse order parameter: 'IN' (in-order), 'PRE' (pre-order) or 'POST' (post-order).
* @returns An array containing elements traversed in the specified order.
*/
dfs(order = 'PRE') {
const result = [];
// Auxiliary recursive function, traverses the binary heap according to the traversal order
const _dfs = (index) => {
const left = 2 * index + 1, right = left + 1;
if (index < this.size) {
if (order === 'IN') {
_dfs(left);
result.push(this.elements[index]);
_dfs(right);
}
else if (order === 'PRE') {
result.push(this.elements[index]);
_dfs(left);
_dfs(right);
}
else if (order === 'POST') {
_dfs(left);
_dfs(right);
result.push(this.elements[index]);
}
}
};
_dfs(0); // Traverse starting from the root node
return result;
}
/**
* Time Complexity: O(n)
* Space Complexity: O(n)
*
* Clone the heap, creating a new heap with the same elements.
* @returns A new Heap instance containing the same elements.
*/
clone() {
return new Heap(this, { comparator: this.comparator, toElementFn: this.toElementFn });
}
/**
* Time Complexity: O(n log n)
* Space Complexity: O(n)
*
* Sort the elements in the heap and return them as an array.
* @returns An array containing the elements sorted in ascending order.
*/
sort() {
const visitedNode = [];
const cloned = new Heap(this, { comparator: this.comparator });
while (cloned.size !== 0) {
const top = cloned.poll();
if (top !== undefined)
visitedNode.push(top);
}
return visitedNode;
}
/**
* Time Complexity: O(n log n)
* Space Complexity: O(n)
*
* Fix the entire heap to maintain heap properties.
*/
fix() {
const results = [];
for (let i = Math.floor(this.size / 2); i >= 0; i--)
results.push(this._sinkDown(i, this.elements.length >> 1));
return results;
}
/**
* Time Complexity: O(n)
* Space Complexity: O(n)
*
* The `filter` function creates a new Heap object containing elements that pass a given callback
* function.
* @param callback - The `callback` parameter is a function that will be called for each element in
* the heap. It takes three arguments: the current element, the index of the current element, and the
* heap itself. The callback function should return a boolean value indicating whether the current
* element should be included in the filtered list
* @param {any} [thisArg] - The `thisArg` parameter is an optional argument that specifies the value
* to be used as `this` when executing the `callback` function. If `thisArg` is provided, it will be
* passed as the `this` value to the `callback` function. If `thisArg` is
* @returns The `filter` method is returning a new `Heap` object that contains the elements that pass
* the filter condition specified by the `callback` function.
*/
filter(callback, thisArg) {
const filteredList = new Heap([], { toElementFn: this.toElementFn, comparator: this.comparator });
let index = 0;
for (const current of this) {
if (callback.call(thisArg, current, index, this)) {
filteredList.add(current);
}
index++;
}
return filteredList;
}
/**
* Time Complexity: O(n)
* Space Complexity: O(n)
*
* The `map` function creates a new heap by applying a callback function to each element of the
* original heap.
* @param callback - The `callback` parameter is a function that will be called for each element in
* the heap. It takes three arguments: `el` (the current element), `index` (the index of the current
* element), and `this` (the heap itself). The callback function should return a value of
* @param comparator - The `comparator` parameter is a function that defines the order of the
* elements in the heap. It takes two elements `a` and `b` as arguments and returns a negative number
* if `a` should be placed before `b`, a positive number if `a` should be placed after
* @param [toElementFn] - The `toElementFn` parameter is an optional function that converts the raw
* element `RR` to the desired type `T`. It takes a single argument `rawElement` of type `RR` and
* returns a value of type `T`. This function is used to transform the elements of the original
* @param {any} [thisArg] - The `thisArg` parameter is an optional argument that allows you to
* specify the value of `this` within the callback function. It is used to set the context or scope
* in which the callback function will be executed. If `thisArg` is provided, it will be used as the
* value of
* @returns a new instance of the `Heap` class with the mapped elements.
*/
map(callback, comparator, toElementFn, thisArg) {
const mappedHeap = new Heap([], { comparator, toElementFn });
let index = 0;
for (const el of this) {
mappedHeap.add(callback.call(thisArg, el, index, this));
index++;
}
return mappedHeap;
}
/**
* The function returns the value of the _comparator property.
* @returns The `_comparator` property is being returned.
*/
get comparator() {
return this._comparator;
}
/**
* The function `_getIterator` returns an iterable iterator for the elements in the class.
*/
*_getIterator() {
for (const element of this.elements) {
yield element;
}
}
/**
* Time Complexity: O(log n)
* Space Complexity: O(1)
*
* Float operation to maintain heap properties after adding an element.
* @param index - The index of the newly added element.
*/
_bubbleUp(index) {
const element = this.elements[index];
while (index > 0) {
const parent = (index - 1) >> 1;
const parentItem = this.elements[parent];
if (this.comparator(parentItem, element) <= 0)
break;
this.elements[index] = parentItem;
index = parent;
}
this.elements[index] = element;
return true;
}
/**
* Time Complexity: O(log n)
* Space Complexity: O(1)
*
* Sinking operation to maintain heap properties after removing the top element.
* @param index - The index from which to start sinking.
* @param halfLength
*/
_sinkDown(index, halfLength) {
const element = this.elements[index];
while (index < halfLength) {
let left = (index << 1) | 1;
const right = left + 1;
let minItem = this.elements[left];
if (right < this.elements.length && this.comparator(minItem, this.elements[right]) > 0) {
left = right;
minItem = this.elements[right];
}
if (this.comparator(minItem, element) >= 0)
break;
this.elements[index] = minItem;
index = left;
}
this.elements[index] = element;
return true;
}
}
exports.Heap = Heap;
class FibonacciHeapNode {
/**
* The constructor function initializes an object with an element and a degree, and sets the marked
* property to false.
* @param {E} element - The "element" parameter represents the value or data that will be stored in
* the node of a data structure. It can be any type of data, such as a number, string, object, or
* even another data structure.
* @param [degree=0] - The degree parameter represents the degree of the element in a data structure
* called a Fibonacci heap. The degree of a node is the number of children it has. By default, the
* degree is set to 0 when a new node is created.
*/
constructor(element, degree = 0) {
this.element = element;
this.degree = degree;
this.marked = false;
}
}
exports.FibonacciHeapNode = FibonacciHeapNode;
class FibonacciHeap {
/**
* The constructor function initializes a FibonacciHeap object with an optional comparator function.
* @param [comparator] - The `comparator` parameter is an optional argument that represents a
* function used to compare elements in the FibonacciHeap. If a comparator function is provided, it
* will be used to determine the order of elements in the heap. If no comparator function is
* provided, a default comparator function will be used.
*/
constructor(comparator) {
this._size = 0;
this.clear();
this._comparator = comparator || this._defaultComparator;
if (typeof this.comparator !== 'function') {
throw new Error('FibonacciHeap constructor: given comparator should be a function.');
}
}
/**
* The function returns the root node of a Fibonacci heap.
* @returns The method is returning either a FibonacciHeapNode object or undefined.
*/
get root() {
return this._root;
}
/**
* The function returns the size of an object.
* @returns The size of the object, which is a number.
*/
get size() {
return this._size;
}
/**
* The function returns the minimum node in a Fibonacci heap.
* @returns The method is returning the minimum node of the Fibonacci heap, which is of type
* `FibonacciHeapNode<E>`. If there is no minimum node, it will return `undefined`.
*/
get min() {
return this._min;
}
/**
* The function returns the comparator used for comparing elements.
* @returns The `_comparator` property of the object.
*/
get comparator() {
return this._comparator;
}
/**
* Get the size (number of elements) of the heap.
* @returns {number} The size of the heap. Returns 0 if the heap is empty. Returns -1 if the heap is invalid.
*/
clear() {
this._root = undefined;
this._min = undefined;
this._size = 0;
}
/**
* Time Complexity: O(1)
* Space Complexity: O(1)
*
* Insert an element into the heap and maintain the heap properties.
* @param element
* @returns {FibonacciHeap<E>} FibonacciHeap<E> - The heap itself.
*/
add(element) {
return this.push(element);
}
/**
* Time Complexity: O(1)
* Space Complexity: O(1)
*
* Insert an element into the heap and maintain the heap properties.
* @param element
* @returns {FibonacciHeap<E>} FibonacciHeap<E> - The heap itself.
*/
push(element) {
const node = this.createNode(element);
node.left = node;
node.right = node;
this.mergeWithRoot(node);
if (!this.min || this.comparator(node.element, this.min.element) <= 0) {
this._min = node;
}
this._size++;
return this;
}
/**
* Time Complexity: O(1)
* Space Complexity: O(1)
*
* Peek at the top element of the heap without removing it.
* @returns The top element or undefined if the heap is empty.
* @protected
*/
peek() {
return this.min ? this.min.element : undefined;
}
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(1)
*
* Get the size (number of elements) of the heap.
* @param {FibonacciHeapNode<E>} head - The head of the linked list.
* @protected
* @returns FibonacciHeapNode<E>[] - An array containing the elements of the linked list.
*/
consumeLinkedList(head) {
const elements = [];
if (!head)
return elements;
let node = head;
let flag = false;
while (true) {
if (node === head && flag)
break;
else if (node === head)
flag = true;
if (node) {
elements.push(node);
node = node.right;
}
}
return elements;
}
/**
* Time Complexity: O(1)
* Space Complexity: O(1)
*
* @param parent
* @param node
*/
mergeWithChild(parent, node) {
if (!parent.child) {
parent.child = node;
}
else {
node.right = parent.child.right;
node.left = parent.child;
parent.child.right.left = node;
parent.child.right = node;
}
}
/**
* Time Complexity: O(log n)
* Space Complexity: O(1)
*
* Remove and return the top element (the smallest or largest element) from the heap.
* @returns The top element or undefined if the heap is empty.
*/
poll() {
return this.pop();
}
/**
* Time Complexity: O(log n)
* Space Complexity: O(1)
*
* Remove and return the top element (the smallest or largest element) from the heap.
* @returns The top element or undefined if the heap is empty.
*/
pop() {
if (this._size === 0)
return undefined;
const z = this.min;
if (z.child) {
const elements = this.consumeLinkedList(z.child);
for (const node of elements) {
this.mergeWithRoot(node);
node.parent = undefined;
}
}
this.removeFromRoot(z);
if (z === z.right) {
this._min = undefined;
this._root = undefined;
}
else {
this._min = z.right;
this._consolidate();
}
this._size--;
return z.element;
}
/**
* Time Complexity: O(1)
* Space Complexity: O(1)
*
* merge two heaps. The heap that is merged will be cleared. The heap that is merged into will remain.
* @param heapToMerge
*/
merge(heapToMerge) {
if (heapToMerge.size === 0) {
return; // Nothing to merge
}
// Merge the root lists of the two heaps
if (this.root && heapToMerge.root) {
const thisRoot = this.root;
const otherRoot = heapToMerge.root;
const thisRootRight = thisRoot.right;
const otherRootLeft = otherRoot.left;
thisRoot.right = otherRoot;
otherRoot.left = thisRoot;
thisRootRight.left = otherRootLeft;
otherRootLeft.right = thisRootRight;
}
// Update the minimum node
if (!this.min || (heapToMerge.min && this.comparator(heapToMerge.min.element, this.min.element) < 0)) {
this._min = heapToMerge.min;
}
// Update the size
this._size += heapToMerge.size;
// Clear the heap that was merged
heapToMerge.clear();
}
/**
* Create a new node.
* @param element
* @protected
*/
createNode(element) {
return new FibonacciHeapNode(element);
}
/**
* Default comparator function used by the heap.
* @param {E} a
* @param {E} b
* @protected
*/
_defaultComparator(a, b) {
if (a < b)
return -1;
if (a > b)
return 1;
return 0;
}
/**
* Time Complexity: O(1)
* Space Complexity: O(1)
*
* Merge the given node with the root list.
* @param node - The node to be merged.
*/
mergeWithRoot(node) {
if (!this.root) {
this._root = node;
}
else {
node.right = this.root.right;
node.left = this.root;
this.root.right.left = node;
this.root.right = node;
}
}
/**
* Time Complexity: O(1)
* Space Complexity: O(1)
*
* Remove and return the top element (the smallest or largest element) from the heap.
* @param node - The node to be removed.
* @protected
*/
removeFromRoot(node) {
if (this.root === node)
this._root = node.right;
if (node.left)
node.left.right = node.right;
if (node.right)
node.right.left = node.left;
}
/**
* Time Complexity: O(1)
* Space Complexity: O(1)
*
* Remove and return the top element (the smallest or largest element) from the heap.
* @param y
* @param x
* @protected
*/
_link(y, x) {
this.removeFromRoot(y);
y.left = y;
y.right = y;
this.mergeWithChild(x, y);
x.degree++;
y.parent = x;
}
/**
* Time Complexity: O(n log n)
* Space Complexity: O(n)
*
* Remove and return the top element (the smallest or largest element) from the heap.
* @protected
*/
_consolidate() {
const A = new Array(this._size);
const elements = this.consumeLinkedList(this.root);
let x, y, d, t;
for (const node of elements) {
x = node;
d = x.degree;
while (A[d]) {
y = A[d];
if (this.comparator(x.element, y.element) > 0) {
t = x;
x = y;
y = t;
}
this._link(y, x);
A[d] = undefined;
d++;
}
A[d] = x;
}
for (let i = 0; i < this._size; i++) {
if (A[i] && this.comparator(A[i].element, this.min.element) <= 0) {
this._min = A[i];
}
}
}
}
exports.FibonacciHeap = FibonacciHeap;
;