UNPKG

dastal

Version:

Data Structures & Algorithms implementations

313 lines 9.82 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.LinkedList = void 0; const numberUtils_1 = require("src/math/numberUtils"); const utils_1 = require("./utils"); /** * A (circular) linked list implementation of the {@link List} interface. */ class LinkedList { /** * Instantiate the list. * * @param elements - A set of elements to initialize the list with. */ constructor(elements) { this.length = 0; this.root = {}; this.root.next = this.root; this.tail = this._addAll(this.root, elements ?? []); } add(index, value) { if (index >= 0 && index < this.length) { const prev = this._get(index - 1); prev.next = { value, next: prev.next }; ++this.length; } else if (index === this.length) { this.push(value); } return this.length; } addAll(index, elements) { if (index >= 0 && index < this.length) { this._addAll(this._get(index - 1), elements); } else if (index === this.length) { this.tail = this._addAll(this.tail, elements); } return this.length; } clear() { this.length = 0; this.tail = this.root.next = this.root; } concat(...lists) { const out = new LinkedList(this); for (const list of lists) { out.addAll(out.size, list); } return out; } copyWithin(index, min, max) { // Check if copying to itself 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; } // Copy to earlier in the list if (index < min) { const node = this._get(index - 1); this._copyWithin(this._get(min - index - 1, node), node, max - min); return this; } // Copy to later in the list if (index > max) { const node = this._get(min - 1); this._copyWithin(node, this._get(index - min - 1, node), max - min); return this; } // Copy to overlapping destination const nodeA = this._get(min - 1); const nodeC = this._get(max - min - 1, nodeA); const nodeD = this._copyWithin(nodeA, nodeC, index - min); if (index + (max - min) >= this.length) { this.tail = nodeC; } const temp = nodeA.next; nodeA.next = nodeC.next; nodeC.next = nodeD.next; nodeD.next = temp; 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) { if (index < 0 || index >= this.length) { return undefined; } return index < this.length - 1 ? this._get(index).value : this.tail.value; } getSet(index, callback) { if (index < 0 || index >= this.length) { return undefined; } const node = index < this.length - 1 ? this._get(index) : this.tail; const value = node.value; node.value = callback(node.value); return value; } pop() { if (this.length < 1) { return undefined; } const value = this.tail.value; this.tail = this._get(this.length - 2); this.tail.next = this.root; --this.length; return value; } push(value) { const tail = { next: this.root, value }; this.tail.next = tail; this.tail = tail; return ++this.length; } remove(index) { if (index < 0 || index >= this.length) { return undefined; } const prev = this._get(index - 1); const node = prev.next; prev.next = node.next; if (index === --this.length) { this.tail = prev; } 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); this.tail = max >= this.length ? root.next : this.tail; const tail = root.next; let prev = tail; let node = tail.next; while (++min < max) { const next = node.next; node.next = prev; prev = node; node = next; } root.next = prev; tail.next = node; 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() { return this.remove(0); } get size() { return this.length; } slice(min, max) { return new LinkedList(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 deleted = new LinkedList(); if (elements == null && count < 1) { return deleted; } // Delete elements let prev = this._get(start - 1); const newTail = start + count >= this.size; while (count-- > 0) { const node = prev.next; deleted.push(node.value); prev.next = node.next; --this.length; } // Add elements prev = this._addAll(prev, elements ?? []); this.tail = newTail ? prev : this.tail; return deleted; } sort(compareFn) { if (this.length > 1) { const [head, tail] = utils_1.linkedMergeSort(this.root.next, this.length, false, compareFn); this.root.next = head; this.tail = 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) { return this.add(0, value); } 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(prev, elements) { const next = prev.next; for (const value of elements) { const node = { value }; prev.next = node; prev = node; ++this.length; } prev.next = next; return prev; } /** * Copy values from 'from' to 'to'. * * @param from - The initial node to copy from * @param prev - The root of the initial node to copy to * @param count - The number of values to copy */ _copyWithin(from, to, count) { while (count-- > 0) { from = from.next; to = to.next; to.value = from.value; } return to; } /** * Get the node at the given index. * * @param index - The index to retrieve * * @returns The node at the given index */ _get(index, root = this.root) { let node = root; while (index-- >= 0) { node = node.next; } return node; } } exports.LinkedList = LinkedList; //# sourceMappingURL=linkedList.js.map