InterviewKata
Data structure 5 of 12

Linked lists

A linked list keeps its order with arrows instead of positions. Each node holds a value and a pointer to the next node. There's no index, so you walk from the front, but adding or removing a node is just changing an arrow or two.

A 3-minute conversation: Sam asks, Priya explains
0:00 / 2:51

1. What it is

A treasure hunt. Each clue tells you where the next clue is hidden. To reach clue 5 you must follow clues 1 to 4; there's no shortcut. But slipping a new clue into the hunt is easy: rewrite one clue to point at the new one, and make the new one point where the old one used to.

A linked list is a chain of nodes. Each node stores a value and next, a reference to the following node; the last node's next is None (null). You hold on to the first node, the head. A doubly linked list also gives each node a prev pointer, so you can walk both ways.

2. How it sits in memory

Unlike an array, the nodes can be anywhere in memory. Each one remembers the address of the next. That's why you can't jump to node i: you only know where node i is once node i − 1 tells you.

Inserting after a node changes two arrows, and the order matters: first point the new node at the rest of the list, then point the old node at the new one. Do it the other way round and the rest of the list is lost.

3. Operations and what they cost

OperationCostCompared with an array
Read the i-th valueO(n)Array: O(1)
Insert or remove at the headO(1)Array: O(n) (everything shifts)
Insert or remove after a node you haveO(1)Array: O(n)
Add at the tailO(1) if you keep a tail pointer, else O(n)Array: O(1) amortised
Search for a valueO(n)Array: O(n)
Remove a node you have (doubly linked)O(1)Its prev pointer finds the node before it.
The dummy nodeMany list bugs are about the head changing. Put a fake node in front (dummy.next = head), work after it, and return dummy.next: the head is no longer a special case.

4. In Python, TypeScript and Java

Python and TypeScript have no built-in singly linked list, so interview problems define the node class for you. Java has LinkedList (doubly linked), but problems still use a ListNode.

class ListNode:
        def __init__(self, val=0, next=None):
            self.val = val
            self.next = next

    head = ListNode(1, ListNode(2, ListNode(3)))   # 1 → 2 → 3
    node = head
    while node:                  # walk to the end
        print(node.val)
        node = node.next

    from collections import deque  # a ready-made doubly linked queue
    q = deque([1, 2, 3]); q.appendleft(0); q.pop()

5. Core operations, step by step

Three moves cover almost every linked-list problem: walk it, splice a node in, and cut a node out.

A · Walk the list

What it doesFollows next from the head until it reaches None, doing something at each node.
Picture itFollowing the treasure-hunt clues one by one, adding up the coins at each.
CostO(n) time, O(1) space.

The code. node is your finger on the current clue.

def total(head):
    s = 0
    node = head
    while node:
        s += node.val
        node = node.next
    return s

Line by line.

LineWhat it doesWhy
while node:Until we fall off the end.The last node's next is None, which stops the loop.
node = node.nextMove to the next node.Forget this line and the loop never ends.

Watch it run. 4 → 7 → 1 → 3.

Watch out for:

  • Moving head itself. head = head.next loses the start of the list. Walk with a separate variable.
  • Reading node.next.val without checking node.next. The last node's next is None.

B · Insert after a node

What it doesSplices a new node in after prev by changing two pointers.
Picture itRewrite clue 2 to point at the new clue, and make the new clue point where clue 2 used to.
CostO(1) once you have prev (finding it may cost O(n)).

The code. New node first points at the rest; then the old node points at the new one.

def insert_after(head, target, val):
    prev = head
    while prev.val != target:
        prev = prev.next
    node = ListNode(val)
    node.next = prev.next
    prev.next = node
    return head

Line by line.

LineWhat it doesWhy
node.next = prev.nextThe new node points at the rest of the list.Do this first, while prev.next still knows where the rest is.
prev.next = nodeThe old node points at the new one.Swapping these two lines makes node point at itself and loses the rest.

Watch it run. Insert 9 after 7 in 4 → 7 → 1.

Watch out for:

  • Linking in the wrong order. Set the new node's next first, then the old node's next.

C · Delete a node (with a dummy in front)

What it doesRemoves the first node holding val by pointing the node before it past it.
Picture itRewrite the clue before the unwanted one so it skips straight past it.
CostO(n) to find it, O(1) to remove it.

The code. A dummy node in front means deleting the head needs no special case.

def delete_value(head, val):
    dummy = ListNode(0, head)
    prev = dummy
    while prev.next and prev.next.val != val:
        prev = prev.next
    if prev.next:
        prev.next = prev.next.next
    return dummy.next

Line by line.

LineWhat it doesWhy
dummy = ListNode(0, head)A fake node in front of the head.Now every real node, even the head, has a node before it.
while prev.next and prev.next.val != valStop at the node before the one to delete.You need the node before it to change its arrow.
prev.next = prev.next.nextPoint past the unwanted node.Nothing points at it any more; it's gone.
return dummy.nextThe real head.If the old head was deleted, dummy.next is the new head.

Watch it run. Delete 1 from 4 → 7 → 1 → 3.

Watch out for:

  • Stopping on the node to delete. In a singly linked list you can't reach the node before it any more. Stop one early.
  • Returning head instead of dummy.next. Wrong when the head itself was deleted.

6. How to remember it

Memory hook: "The treasure hunt"Every clue points to the next. Follow clues to get anywhere; rewrite one clue to add or skip a stop. Before rewriting, make sure the new clue knows where the hunt continues.

Picture

Paper clues scattered around a park, each with an arrow drawn to the next.

Chant

"Walk with a finger, link the new one first, stop one early to delete."

Key rule

The head might change? Put a dummy node in front.

7. Problems solved with it

Two easy problems using exactly the moves above.

LC 1290 · Convert Binary Number in a Linked List to Integer

The problemEach node holds 0 or 1; the list is a binary number with the most significant bit first. Return its value.
What it doesWalks once; each new bit doubles the value so far and adds the bit.
CostO(n) time, O(1) space.

The code. Operation A with a different job at each node.

def get_decimal_value(head):
    value = 0
    node = head
    while node:
        value = value * 2 + node.val
        node = node.next
    return value

Line by line.

LineWhat it doesWhy
value = value * 2 + node.valShift left one binary place and add the new bit.The same idea as reading a decimal number digit by digit: value × 10 + digit.

Watch it run. 1 → 0 → 1 → 1 (binary 1011).

Watch out for:

  • Collecting the bits in a string first. Works, but the running value needs no extra space.

LC 203 · Remove Linked List Elements

The problemRemove every node whose value equals val and return the new head.
What it doesOperation C, but keeps going after each deletion.
CostO(n) time, O(1) space.

The code. Only move prev forward when its next node is a keeper.

def remove_elements(head, val):
    dummy = ListNode(0, head)
    prev = dummy
    while prev.next:
        if prev.next.val == val:
            prev.next = prev.next.next
        else:
            prev = prev.next
    return dummy.next

Line by line.

LineWhat it doesWhy
if prev.next.val == valIs the next node unwanted?Look one ahead so you can still change prev's arrow.
prev.next = prev.next.nextSkip it, but don't move prev.The new next might also need deleting (6, 6).
prev = prev.nextA keeper: step forward.

Watch it run. Remove 6 from 6 → 1 → 6 → 6 → 2.

Watch out for:

  • Moving prev after a deletion. Two unwanted nodes in a row: the second is skipped over and survives.

More practice, solved on the pattern pages

#ProblemLevelWhere
206Reverse Linked ListEasySolved step by step on 05 · LinkedList In-place Reversal
21Merge Two Sorted ListsEasySolved step by step on 05 · LinkedList In-place Reversal
83Remove Duplicates from Sorted ListEasySolved step by step on 05 · LinkedList In-place Reversal
876Middle of the Linked ListEasySolved step by step on 04 · Fast & Slow Pointers
141Linked List CycleEasySolved step by step on 04 · Fast & Slow Pointers
234Palindrome Linked ListEasySolved step by step on 04 · Fast & Slow Pointers

8. Common mistakes

Losing the rest of the listChange prev.next before saving where it pointed, and everything after is unreachable. Link the new node first, or keep the old next in a variable.
Moving headWalk with node = head; keep head pointing at the start.
Forgetting the empty listhead can be None. A dummy node, or an early if not head: return …, handles it.
Reading past the endnode.next.val crashes on the last node. Check node.next first.
Infinite loopsA missing node = node.next, or a cycle you created by mistake, never ends. Draw the arrows on paper before coding.

9. Self-check quiz

Reading the 1,000th value of a linked list costs…

There's no address formula; you follow 999 arrows.

Which order inserts node after prev safely?

The other order makes node.next point at node itself and loses the rest.

Why put a dummy node in front of the head?

Every real node now has a node before it; return dummy.next at the end.

Which is O(1) for a linked list but O(n) for an array?

Arrays shift every element; a list changes one arrow.

10. Where it shows up next

Next structure: 06 · Stacks, last in, first out.