Skip to content

Instantly share code, notes, and snippets.

@rbuckton
Last active October 20, 2017 00:39
Show Gist options
  • Select an option

  • Save rbuckton/a7736d4c061446df6590c4b5dfb2dccd to your computer and use it in GitHub Desktop.

Select an option

Save rbuckton/a7736d4c061446df6590c4b5dfb2dccd to your computer and use it in GitHub Desktop.
export class LinkedList {
#head;
#size;
get first() { return this.#head; }
get last() { return this.#head !== undefined ? this.#head.LinkedListNode#previous : undefined; }
get size() { return this.#size; }
addLast(value) {
return this.#insertBefore(this.#head, new LinkedListNode(value), /*replaceHead*/ false);
}
addNodeLast(node) {
this.#checkNewNode(node);
this.#insertBefore(this.#head, node, /*replaceHead*/ false);
}
addFirst(value) {
return this.#insertBefore(this.#head, new LinkedListNode(value), /*replaceHead*/ true);
}
addNodeFirst(node) {
this.#checkNewNode(node);
this.#insertBefore(this.#head, node, /*replaceHead*/ true);
}
addBefore(node, value) {
this.#checkNode(node);
return this.#insertBefore(node, new LinkedListNode(value), /*replaceHead*/ this.#head === node);
}
addNodeBefore(node, newNode) {
this.#checkNode(node);
this.#checkNewNode(newNode);
this.#insertBefore(node, newNode, /*replaceHead*/ this.#head === node);
}
addAfter(node, value) {
this.#checkNode(node);
return this.insertBefore(node.LinkedListNode#next, new LinkedListNode(value), /*replaceHead*/ false);
}
addNodeAfter(node, newNode) {
this.#checkNode(node);
this.#checkNewNode(newNode);
this.#insertBefore(node.LinkedListNode#next, newNode, /*replaceHead*/ false);
}
has(value) {
return this.find(value) !== undefined;
}
find(value) {
for (let node = this.first; node !== undefined; node = node.next) {
if (node.value === value) return node;
}
return undefined;
}
findLast(value) {
for (let node = this.last; node !== undefined; node = node.previous) {
if (node.value === value) return node;
}
return undefined;
}
delete(value) {
const node = this.find(value);
if (node) {
this.#delete(node);
return true;
}
return false;
}
deleteFirst() {
if (this.#head !== undefined) {
return this.#delete(this.#head).value;
}
return undefined;
}
deleteLast() {
if (this.#head !== undefined) {
return this.#delete(this.#head.LinkedListNode#previous).value;
}
return undefined;
}
deleteNode(node) {
this.#checkNode(node);
this.#delete(node);
}
deleteNodeFirst() {
if (this.#head !== undefined) {
this.#delete(this.#head);
return true;
}
return false;
}
deleteNodeLast() {
if (this.#head !== undefined) {
this.#delete(this.#head.LinkedListNode#previous);
return true;
}
return false;
}
clear() {
let next = this.#head;
while (next !== undefined) {
const node = next;
next = node.LinkedListNode#next;
this.#invalidate(node);
if (next === this.#head) break;
}
this.#size = 0;
}
forEach(callback, thisArg) {
let next = this.#head;
while (next !== undefined) {
const node = next;
next = node.LinkedListNode#next;
callback.call(thisArg, node.value, node, this);
if (next === this.#head || next.list !== this) break;
}
}
*values() {
let next = this.#head;
while (next !== undefined) {
const node = next;
next = node.LinkedListNode#next;
yield node.value;
if (next === this.#head || next.list !== this) break;
}
}
*nodes() {
let next = this.#head;
while (next !== undefined) {
const node = next;
next = node.LinkedListNode#next;
yield node;
if (next === this.#head || next.list !== this) break;
}
}
[Symbol.iterator]() { return this.values(); }
#checkNode(node) {
if (!node) throw new TypeError("Argument not optional: node");
if (node.list !== this) throw new Error("Wrong list.");
}
#checkNewNode(newNode) {
if (!newNode) throw new TypeError("Argument not optional: newNode");
if (newNode.list) throw new Error("Node is already attached to a list.");
}
#insertBefore(node, newNode, replaceHead) {
newNode.#LinkedListNode#list = this;
if (node === undefined) {
newNode.LinkedListNode#next = newNode;
newNode.LinkedListNode#previous = newNode;
this.#head = newNode;
}
else {
newNode.LinkedListNode#next = node;
newNode.LinkedListNode#previous = node.LinkedListNode#previous;
node.LinkedListNode#previous.LinkedListNode#next = newNode;
node.LinkedListNode#previous = newNode;
if (replaceHead) this.#head = newNode;
}
this.#size++;
return newNode;
}
#delete(node) {
if (node.LinkedListNode#next === node) {
this.#head = undefined;
}
else {
node.LinkedListNode#next.LinkedListNode#previous = node.LinkedListNode#previous;
node.LinkedListNode#previous.LinkedListNode#next = node.LinkedListNode#next;
if (this.#head === node) {
this.#head = node.LinkedListNode#next;
}
}
this.#invalidate(node);
this.#size--;
return node;
}
#invalidate(node) {
node.LinkedListNode#list = undefined;
node.LinkedListNode#next = undefined;
node.LinkedListNode#previous = undefined;
}
}
export class LinkedListNode friend LinkedList {
value;
#list;
#previous;
#next;
constructor(value) { this.value = value; }
get list() { return this.#list; }
get previous() { return this.#previous !== undefined && this !== this.#list.first ? this.#previous : undefined; }
get next() { return this.#next !== undefined && this !== this.#list.last ? this.#next : undefined; }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment