dastal
Version:
Data Structures & Algorithms implementations
313 lines • 9.82 kB
JavaScript
"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