class LinkedNode<T> {
value: T;
next: LinkedNode<T>
constructor(value: T) {
this.value = value
}
}
interface ILinkedList<T> {
append(value: T): void
traverse(): void
insert(value: T, position: number): boolean
removeAt(position: number): T | null
get(position: number): T | null
update(value: T, position: number): boolean
indexOf(value: T): number
remove(value: T): T | null
isEmpty(): boolean
size(): number
}
class LinkedList<T> implements ILinkedList<T> {
protected head: LinkedNode<T> | null = null;
protected tail: LinkedNode<T> | null = null
protected length: number = 0
protected getNode(position: number): {
previous: LinkedNode<T> | null
current: LinkedNode<T> | null
} {
let index = 0
let previous: LinkedNode<T> | null = null
let current = this.head
while(index++ < position && current) {
previous = current
current = current.next
}
return {
previous,
current
}
}
private isTail(node: LinkedNode<T>) {
return this.tail === node
}
append(value: T) : void {
const newNode = new LinkedNode(value)
if(!this.head) {
this.head = newNode
} else {
this.tail!.next = newNode
}
this.tail = newNode
this.length ++
}
traverse(): void {
let values: T[] = []
let current = this.head
while(current) {
values.push(current.value)
current = this.isTail(current) ? null : current.next
}
if(this.head && this.tail!.next === this.head) {
values.push(this.head.value)
}
console.log(this.length, values.join(" -> "))
}
insert(value: T, position: number): boolean {
if (position < 0 && position > this.length) return false;
const newNode = new LinkedNode(value);
let { previous, current } = this.getNode(position);
if (position === 0) {
newNode.next = this.head;
this.head = newNode;
} else {
newNode.next = current;
previous!.next = newNode;
if (position === this.length) {
this.tail = newNode;
}
}
this.length++;
return true;
}
removeAt(position: number): T | null {
if(position < 0 && position > this.length) return null
let { previous, current } = this.getNode(position);
if(position === 0) {
this.head = current.next ?? null
if(this.length === 1) {
this.tail = null
}
} else {
previous!.next = current.next ?? null
if(current === this.tail) {
this.tail = previous
}
}
this.length--
return current?.value ?? null
}
get(position: number): T | null {
if(position < 0 || position > this.length) return null
let { current } = this.getNode(position)
return current?.value ?? null
}
update(value: T, position: number): boolean {
if(position < 0 || position > this.length) return false
let {current} = this.getNode(position)
current!.value = value;
return true
}
indexOf(value: T): number {
let index = 0
let current = this.head
while(current) {
if(current.value === value) return index
current = this.isTail(current) ? null : current.next
index++
}
return -1
}
remove(value: T): T | null {
let index = this.indexOf(value)
return this.removeAt(index)
}
peek(): T | undefined {
return this.head?.value
}
isEmpty(): boolean {
return this.length === 0
}
size(): number {
return this.length
}
}
const linked = new LinkedList<string>();
linked.append("aaa");
linked.append("bbb");
linked.append("ccc");
linked.traverse();
linked.insert("zzz", 0);
linked.insert("ddd", 2);
linked.insert("eee", 5);
linked.traverse();
console.log(linked.removeAt(0));
console.log(linked.removeAt(1));
console.log(linked.removeAt(3));
linked.traverse();
console.log(linked.get(0));
console.log(linked.get(1));
console.log(linked.get(2));
console.log(linked.get(3));
console.log(linked.update("aa", 0));
console.log(linked.update("cc", 2));
console.log(linked.update("dd", 3));
linked.traverse();
console.log(linked.indexOf("aa"));
console.log(linked.indexOf("ccc"));
linked.remove("bbb");
linked.traverse();
console.log(linked.isEmpty());