Lab
Linked list
// ---- Checker plumbing: you don't need to edit this. ----------------------
/**
* A car in the train: its number and the cars on either side. Read and write
* its links through prev and next. The checker counts them here: every read
* of prev or next is a link read (one step along the train), every assignment
* is a link write, changed or not. The train's head and tail are not counted.
*/
class Car {
static reads = 0;
static writes = 0;
readonly id: string;
private prevLink: Car | null;
private nextLink: Car | null;
constructor(id: string) { this.id = id; this.prevLink = null; this.nextLink = null; }
get prev(): Car | null { Car.reads++; return this.prevLink; }
set prev(car: Car | null) { Car.writes++; this.prevLink = car; }
get next(): Car | null { Car.reads++; return this.nextLink; }
set next(car: Car | null) { Car.writes++; this.nextLink = car; }
/** For the checker: cars coupled in order, not counted. */
static chain(ids: string[]): Car[] {
const cars: Car[] = [];
for (const id of ids) cars.push(new Car(id));
for (let i = 0; i < cars.length; i++) { cars[i]!.prevLink = cars[i - 1] ?? null; cars[i]!.nextLink = cars[i + 1] ?? null; }
return cars;
}
/** For the checker: the car numbers from a car onwards, or backwards, not counted. */
static walk(from: Car | null, forward: boolean): string[] {
const ids: string[] = [];
for (let car = from; car && ids.length < 5000; car = forward ? car.nextLink : car.prevLink) ids.push(car.id);
return ids;
}
/** For the checker: whether a car has no links left, not counted. */
static detached(car: Car): boolean {
return car.prevLink === null && car.nextLink === null;
}
}
// ---- End of checker plumbing. --------------------------------------------
/** A freight train as a doubly linked list of cars (above), from the locomotive to the last car. */
class Train {
head: Car | null = null;
tail: Car | null = null;
/** Couples a car at the end of the train and returns it: the handle. */
couple(id: string): Car {
const car = new Car(id);
if (this.tail) {
this.tail.next = car;
car.prev = this.tail;
} else {
this.head = car;
}
this.tail = car;
return car;
}
/** The first car with this number, walking from the locomotive. */
find(id: string): Car | null {
for (let car = this.head; car; car = car.next) {
if (car.id === id) return car;
}
return null;
}
/** Moves one held car to just after anchor (null: the front). */
moveAfter(car: Car, anchor: Car | null): void {
const prev = car.prev;
const next = car.next;
if (car === anchor || prev === anchor) return;
// Unhook the car: its old neighbours join up.
if (prev) prev.next = next;
else this.head = next;
if (next) next.prev = prev;
else this.tail = prev;
// Hook it in after the anchor (or at the front).
const after = anchor ? anchor.next : this.head;
car.prev = anchor;
car.next = after;
if (after) after.prev = car;
else this.tail = car;
if (anchor) anchor.next = car;
else this.head = car;
}
/** Takes car out of the train: its neighbours join up, and its own links are cleared. */
remove(car: Car): void {
// TODO: uncouple the car.
}
}