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