UNPKG

dastal

Version:

Data Structures & Algorithms implementations

308 lines 9.72 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DoublyLinkedList = void 0; const numberUtils_1 = require("src/math/numberUtils"); const utils_1 = require("./utils"); /** * A (circular) doubly-linked list implementation of the {@link List} interface. * * Operations that index into the list will traverse the list from the beginning or the end, whichever is closer to the specified index. */ class DoublyLinkedList { /** * Instantiate the list. * * @param elements - A set of elements to initialize the list with. */ constructor(elements) { this.length = 0; this.root = {}; this.root.prev = this.root.next = this.root; this._addAll(this.root, elements ?? []); } add(index, value) { if (index < 0 || index > this.length) { return this.length; } const prev = this._get(index - 1); const node = { next: prev.next, prev, value }; prev.next = node; node.next.prev = node; return ++this.length; } addAll(index, elements) { if (index >= 0 && index <= this.length) { this._addAll(this._get(index), elements); } return this.length; } clear() { this.length = 0; this.root.prev = this.root.next = this.root; } concat(...lists) { const out = new DoublyLinkedList(this); for (const list of lists) { out.addAll(out.size, list); } return out; } copyWithin(index, min, max) { // Check if copying to the same section index = numberUtils_1.clamp(numberUtils_1.wrapLeft(index, 0, this.length), 0, this.length); min = numberUtils_1.clamp(numberUtils_1.wrapLeft(min ?? 0, 0, this.length), 0, this.length); if (min === index) { return this; } // Check if the section to copy has no length max = numberUtils_1.clamp(numberUtils_1.wrapLeft(max ?? this.length, 0, this.length), 0, this.length); max = min + Math.min(max - min, this.length - index); if (min >= max) { return this; } // Check for overlap edge case if (min < index && index < max) { let nodeA = this._get(max); let nodeB = this._get(index + (max - min)); do { nodeA = nodeA.prev; nodeB = nodeB.prev; nodeB.value = nodeA.value; } while (++min < max); return this; } // Copy the section to the destination let nodeA = this._get(min); let nodeB = this._get(index); do { nodeB.value = nodeA.value; nodeA = nodeA.next; nodeB = nodeB.next; } while (++min < max); return this; } fill(element, min, max) { min = numberUtils_1.clamp(numberUtils_1.wrapLeft(min ?? 0, 0, this.length), 0, this.length); max = numberUtils_1.clamp(numberUtils_1.wrapLeft(max ?? this.length, 0, this.length), 0, this.length); if (min < max) { let node = this._get(min); do { node.value = element; node = node.next; } while (++min < max); } return this; } get(index) { return index < 0 || index >= this.length ? undefined : this._get(index).value; } getSet(index, callback) { if (index < 0 || index >= this.length) { return undefined; } const node = this._get(index); const value = node.value; node.value = callback(node.value); return value; } pop() { if (this.length < 1) { return undefined; } const tail = this.root.prev; tail.prev.next = this.root; this.root.prev = tail.prev; --this.length; return tail.value; } push(value) { const prev = this.root.prev; const node = { next: this.root, prev, value }; prev.next = this.root.prev = node; return ++this.length; } remove(index) { if (index < 0 || index >= this.length) { return undefined; } const node = this._get(index); node.prev.next = node.next; node.next.prev = node.prev; --this.length; return node.value; } reverse(min, max) { min = numberUtils_1.clamp(numberUtils_1.wrapLeft(min ?? 0, 0, this.length), 0, this.length); max = numberUtils_1.clamp(numberUtils_1.wrapLeft(max ?? this.length, 0, this.length), 0, this.length); if (max - min < 2) { return this; } const root = this._get(min - 1); const tail = root.next; let node = tail; do { const temp = node.next; node.next = node.prev; node.prev = temp; root.next = node; node = temp; } while (++min < max); tail.next = node; node.prev = tail; root.next.prev = root; return this; } set(index, element) { if (index < 0 || index >= this.length) { return undefined; } const node = this._get(index); const value = node.value; node.value = element; return value; } shift() { if (this.length < 1) { return undefined; } const head = this.root.next; head.next.prev = this.root; this.root.next = head.next; --this.length; return head.value; } get size() { return this.length; } slice(min, max) { return new DoublyLinkedList(this.view(min, max)); } splice(start, count, elements) { start = numberUtils_1.clamp(numberUtils_1.wrapLeft(start ?? 0, 0, this.length), 0, this.length); count = numberUtils_1.clamp(count ?? this.size, 0, this.size - start); // If not modifying the list const list = new DoublyLinkedList(); if (elements == null && count < 1) { return list; } // Delete elements let node = this._get(start); while (count-- > 0) { list.push(node.value); node.prev.next = node.next; node.next.prev = node.prev; node = node.next; --this.length; } // Add elements this._addAll(node, elements ?? []); return list; } sort(compareFn) { if (this.length > 1) { const [head, tail] = utils_1.linkedMergeSort(this.root.next, this.length, true, compareFn); this.root.next = head; tail.next.prev = tail; } return this; } /** * Receive an iterator through the list. * * **Note:** Unexpected behavior can occur if the collection is modified during iteration. * * @returns An iterator through the list */ *[Symbol.iterator]() { for (let node = this.root.next; node !== this.root; node = node.next) { yield node.value; } } unshift(value) { const head = this.root.next; const node = { next: head, prev: this.root, value }; this.root.next = head.prev = node; return ++this.length; } update(min, max, callback) { if (callback == null) { if (arguments.length < 2) { callback = min; min = undefined; } else { callback = max; max = undefined; } } min = numberUtils_1.clamp(numberUtils_1.wrapLeft(min ?? 0, 0, this.length), 0, this.length); max = numberUtils_1.clamp(numberUtils_1.wrapLeft(max ?? this.length, 0, this.length), 0, this.length); if (min < max) { let node = this._get(min); do { node.value = callback(node.value, min); node = node.next; } while (++min < max); } return this; } *view(min, max) { min = numberUtils_1.clamp(numberUtils_1.wrapLeft(min ?? 0, 0, this.length), 0, this.length); let len; if (max == null) { len = () => this.length; } else if (max >= 0) { len = () => max; } else { len = () => this.length + max; } if (min < len()) { let node = this._get(min); do { yield node.value; node = node.next; } while (++min < len() && node !== this.root); } } /** * */ _addAll(next, elements) { let prev = next.prev; for (const value of elements) { const node = { prev, value }; prev.next = node; prev = node; ++this.length; } prev.next = next; next.prev = prev; } /** * A helper method to iterate and return the node at the given index. * * Depending on the index, the list will be traversed from beginning or end; whichever is closest to the specified index. * * @param index - The index to retrieve * * @returns The node at the given index */ _get(index) { let node = this.root; if (index < this.length / 2) { while (index-- >= 0) { node = node.next; } } else { for (index = this.length - index; index > 0; --index) { node = node.prev; } } return node; } } exports.DoublyLinkedList = DoublyLinkedList; //# sourceMappingURL=doublyLinkedList.js.map