← Linked list lesson
Lab

Linked list

Sign in to run your code

Read the brief and the starter here. With a free account you can edit it, run the sample, step through it and check your work, and your progress is kept.

// ---- 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.
  }
}