· Updated

Singly Linked Lists

Learn how singly linked lists work, including head pointers, next references, traversal, insertion, append, and deletion.

A singly linked list is the simplest linked list.

Each node points in one direction:

head -> [A] -> [B] -> [C] -> null

Each node knows only the next node.

It does not know the previous node.

That one-way movement defines the structure.

The Node

type Node<T> = {
  value: T;
  next: Node<T> | null;
};

And a minimal list can be represented by its head:

type LinkedList<T> = {
  head: Node<T> | null;
};

If head is null, the list is empty.

If head points to a node, traversal starts there.

Traversal

Traversal follows next until the end:

function values<T>(list: LinkedList<T>): T[] {
  const result: T[] = [];
  let current = list.head;

  while (current !== null) {
    result.push(current.value);
    current = current.next;
  }

  return result;
}

There is no index jump.

There is only walking.

Insert At The Head

Inserting at the head is the easiest operation:

head -> [B] -> [C]

Insert A:

head -> [A] -> [B] -> [C]

Code:

function prepend<T>(list: LinkedList<T>, value: T): void {
  const node = {
    value,
    next: list.head,
  };

  list.head = node;
}

The new node points at the old head.

Then the list head points at the new node.

That is it.

Append

Appending means adding to the end:

head -> [A] -> [B] -> null

becomes:

head -> [A] -> [B] -> [C] -> null

Without a tail pointer, you must walk to the last node:

function append<T>(list: LinkedList<T>, value: T): void {
  const node = { value, next: null };

  if (list.head === null) {
    list.head = node;
    return;
  }

  let current = list.head;

  while (current.next !== null) {
    current = current.next;
  }

  current.next = node;
}

Appending is O(n) here because you walk through the list.

If the list also stores a tail reference, append can become O(1).

That is a design choice.

Delete After A Node

If you have the previous node, deletion is local:

prev -> target -> next

becomes:

prev -> next

Code:

function deleteAfter<T>(node: Node<T>): void {
  if (node.next === null) return;
  node.next = node.next.next;
}

The target node is no longer reachable from the list.

In garbage-collected languages, it will eventually be cleaned up.

In manual-memory languages, you would also need to free it safely.

The Main Limitation

A singly linked list moves forward easily.

It does not move backward.

That makes some operations awkward.

If you have a node and want to delete that node, you usually need the previous node.

But the node itself does not know who points to it.

That limitation is why doubly linked lists exist.