Pattern 06 of 20

Linked List

Rewire pointers carefully: dummy heads, fast and slow pointers, in-place reversal.

When to use it

  • The input is a linked list and random access is not available
  • Finding a middle, a cycle or the k-th node from the end
  • Reversing, merging or reordering nodes without extra memory

The idea

Most list problems are about changing next pointers in the right order so nothing is lost. A dummy head removes special cases at the front, and two pointers moving at different speeds find middles and cycles.

Typical cost O(n) time, O(1) extra space.

How it works

A linked list gives you no random access, so every algorithm is a walk. The work is in rewiring next pointers in an order that never loses the rest of the list. Before you overwrite a pointer, save what it pointed to.

A dummy head is the cheapest trick in the pattern. It gives the real head a predecessor, so deleting or inserting at the front looks exactly like doing it in the middle. Return dummy.next at the end.

Fast and slow pointers find structure without extra memory. When the fast pointer moves two steps for each step of the slow one, the slow pointer is at the middle when the fast one reaches the end, and the two meet inside a cycle if one exists. A gap of k nodes between two pointers finds the k-th node from the end.

Many medium problems are compositions of three primitives: find the middle, reverse a sublist, merge two lists. Reorder, palindrome check and k-group reversal are all built from them.

How to attack it

  1. 1Draw three or four nodes and the pointers before writing code.
  2. 2Add a dummy head if the head itself might change.
  3. 3Save next before overwriting any pointer.
  4. 4Break the problem into find-middle, reverse and merge if it looks complicated.
  5. 5Test with an empty list, one node, and two nodes.

Template

A skeleton to adapt, not a solution to paste. Write it from memory a few times.

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


def reverse(head):
    prev = None
    while head:
        nxt = head.next      # save the rest before rewiring
        head.next = prev
        prev, head = head, nxt
    return prev


def middle(head):
    # Slow ends at the middle (second middle for even lengths).
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
    return slow


def remove_value(head, val):
    dummy = ListNode(0, head)  # gives the head a predecessor
    prev = dummy
    while prev.next:
        if prev.next.val == val:
            prev.next = prev.next.next
        else:
            prev = prev.next
    return dummy.next

Variants

In-place reversal
Whole list, a sublist between positions, or every group of k nodes, always with prev, curr and a saved next.
Fast and slow pointers
Middle of the list, cycle detection, and the cycle entry (reset one pointer to the head and step both by one).
Gap pointers
Advance one pointer k steps first, then move both; the trailing one stops just before the k-th node from the end.
Merging
Splice the smaller head onto a tail pointer behind a dummy; extends to k lists with a heap.
Copy with extra pointers
Deep-copying a list with random pointers needs a map from old node to new, or interleaved copies.

Pitfalls

  • Losing the rest of the list by overwriting next before saving it.
  • Special-casing the head instead of using a dummy, and getting one branch wrong.
  • Dereferencing fast.next.next without checking fast.next first.
  • Forgetting to cut the tail (set next to null) after splitting a list, which creates a cycle.
  • Returning the old head after a reversal instead of the new one.

What it costs

OperationCost
Access the k-th nodeO(k)
Insert or delete with a known predecessorO(1)
Reverse, find middle, detect cycleO(n) time, O(1) space
Merge two sorted listsO(n + m) time, O(1) space

31 Linked List problems

Easiest first. Problems marked Free can be solved without Pro.

  1. 1Reverse the ChainFreeEasy
  2. 2Midpoint of the ChainFreeEasy
  3. 3Weave Two Sorted ChainsFreeEasy
  4. 4Collapse Repeated ReadingsEasy
  5. 5Drop Matching NodesEasy
  6. 6Chain Reads the Same Both WaysEasy
  7. 7Binary Chain to NumberEasy
  8. 8Loop DetectorEasy
  9. 9Drop Every K-th NodeEasy
  10. 10Alternate Two ChainsEasy
  11. 11Loop Entry IndexMedium
  12. 12Cut the K-th From the TailMedium
  13. 13Odd Slots Then Even SlotsMedium
  14. 14Add Reversed Digit ChainsMedium
  15. 15Rotate the Chain RightMedium
  16. 16Fold the ChainMedium
  17. 17Pivot Partition of a ChainMedium
  18. 18Reverse a StretchMedium
  19. 19Add Forward Digit ChainsMedium
  20. 20Swap Neighbor PairsMedium
  21. 21Purge Duplicated ValuesMedium
  22. 22Twin Sum PeakMedium
  23. 23Drop the Middle NodeMedium
  24. 24Split a Chain Into K PartsMedium
  25. 25Sort a ChainMedium
  26. 26Flip in Blocks of KHard
  27. 27Merge Many Sorted ChainsHard
  28. 28Flip Blocks From the TailHard
  29. 29Growing Groups, Even FlipHard
  30. 30Nearly Sorted ChainHard
  31. 31Multiply Digit ChainsHard

Solve them in the browser, in Python or JavaScript.