Skip to content

Beginner: foundations · Stacks, queues and linked lists · topic 20 of 61 · 40 min

Fast and slow pointers and merging lists

Explanation

Two pointers walking a linked list at different speeds, or with a fixed gap, answer questions in one pass with O(1) extra space: the middle node, whether the list has a cycle (Floyd's tortoise and hare), where the cycle starts, and the n-th node from the end. A dummy node makes merging two sorted lists short and safe, and the same tools check whether a list is a palindrome.

Two pointers on a linked list

Module 3 used two pointers on arrays. On a linked list you cannot jump to an index, but you can walk two pointers at different speeds, or keep a fixed gap between them. That answers questions about the middle, cycles and the end of the list in one pass, with O(1) extra space.

The middle of a list

Move slow one step and fast two steps at a time. When fast reaches the end, slow has gone half as far, so it is at the middle. With the loop condition while fast and fast.next, an even-length list gives the SECOND of its two middle nodes: for 1 → 2 → 3 → 4 it stops at 3. Write while fast.next and fast.next.next instead to stop at the first middle, 2; that version is used to split a list in half for merge sort.

python
def middle(head):
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
    return slow
# 1 -> 2 -> 3 -> 4 -> 5   gives the node 3
# 1 -> 2 -> 3 -> 4        gives the node 3 (the second middle)

Detecting a cycle: Floyd's tortoise and hare

A cycle means some node's next points back to an earlier node, so walking never reaches None. A plain loop like while node: node = node.next then runs forever. One fix is a set of visited nodes, which costs O(n) extra space. Floyd's algorithm uses O(1): move slow one step and fast two steps. Without a cycle, fast reaches the end. With a cycle, both pointers end up inside it, and each step fast gains exactly one node on slow, so the gap shrinks to zero and they meet.

Think of it like this: Two runners on a circular track, one twice as fast as the other. The faster one must lap the slower one sooner or later. On a straight road that ends, the faster one simply reaches the end first.

Compare nodes with slow is fast, not their values: two different nodes can hold the same value.

Where the cycle starts

After slow and fast meet, put one pointer back at the head and move both ONE step at a time. They meet again exactly at the first node of the cycle. Why: let a be the distance from the head to the cycle start, b the distance from the start to the meeting point, and c the cycle length. Fast walked twice as far as slow: 2(a + b) = a + b + k·c for some whole number of laps k, so a = k·c − b. Walking a steps from the meeting point therefore lands on the cycle start, which is also where a pointer from the head arrives after a steps.

python
def cycle_start(head):
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        if slow is fast:              # they met inside the cycle
            p = head
            while p is not slow:
                p = p.next
                slow = slow.next
            return p                  # first node of the cycle
    return None                       # fast reached the end: no cycle

The n-th node from the end

To remove the n-th node from the end in one pass, keep a gap of n nodes between two pointers. Start both at a dummy node before the head and move fast n + 1 steps ahead. Then move both one step at a time until fast is None. Now slow sits just BEFORE the node to remove, so slow.next = slow.next.next deletes it. The dummy covers the case where the head itself is removed (n equal to the length).

Merging two sorted lists with a dummy node

The array merge from Module 3 works on lists too, without copying values: relink the existing nodes. A dummy node gives the merged list a fixed start, and a tail pointer marks where the next node goes. When one list runs out, attach the rest of the other in one step, because it is already sorted. O(n + m) time and O(1) extra space.

python
dummy = tail = ListNode()
while a and b:
    if a.val <= b.val:
        tail.next, a = a, a.next
    else:
        tail.next, b = b, b.next
    tail = tail.next
tail.next = a or b          # attach whatever is left
merged = dummy.next
The core of merging lists a and b. Taking from a on ties (<=) keeps equal values in their original order.

Palindrome linked list

Is 1 → 2 → 2 → 1 the same backwards? Copying the values into a Python list and comparing it with its reverse works in O(n) time but uses O(n) extra space. The O(1)-space way combines this module's tools: find the middle with slow and fast pointers, reverse the second half (Topic 4), then walk from the head and from the reversed half together, comparing values. Reverse the second half back afterwards if the caller still needs the list.

Common mistakes

  • Writing while fast.next and fast, which crashes when fast is None. Fix: while fast and fast.next (check fast first).
  • Comparing values instead of nodes when looking for a cycle. Fix: slow is fast.
  • An off-by-one gap in n-th from the end. Fix: start at a dummy and move fast n + 1 steps.
  • Forgetting to attach the leftover part after merging. Fix: tail.next = a or b.
  • Not saying which middle you return for even lengths. Fix: state it, then choose the loop condition to match.
  • Printing or converting a list that may have a cycle, which never ends. Fix: detect the cycle first.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Fast and slow pointers and merging lists · 1/8

Fast and slow pointers

This lesson uses two pointers on linked lists, and a dummy node for merging.

Prefer a video by a teacher? Find free videos on “fast and slow pointers linked list cycle detection floyd middle of linked list merge two sorted lists” on YouTube ↗

In real life: Merging statements from two branches

A bank keeps each branch's transactions sorted by time. To show a customer one combined statement, it merges the sorted streams instead of sorting everything again: always take the earlier of the two next entries. That is the merge of two sorted lists, O(n + m). Python's heapq.merge does this lazily for any number of sorted inputs.

python
import heapq

branch_a = [("09:05", "A: deposit"), ("10:30", "A: withdrawal")]
branch_b = [("09:40", "B: cheque"), ("11:15", "B: deposit")]
for time, entry in heapq.merge(branch_a, branch_b):
    print(time, entry)
Shows the four entries in time order: 09:05, 09:40, 10:30, 11:15.

Practice (0/4 done)

Task 1 · Code · Python

Write middle_node(head) that returns the middle NODE of a linked list (the node itself, not its value) using slow and fast pointers in one pass. For an even number of nodes, return the SECOND of the two middle nodes: for 1 -> 2 -> 3 -> 4 return the node holding 3. Return None for an empty list.

Task 2 · Code · Python

Write has_cycle(head) that returns True if the linked list has a cycle (some node's next points back to an earlier node), else False. Use Floyd's slow and fast pointers: O(n) time and O(1) extra space (no set of visited nodes). Careful: a plain "while node: node = node.next" loop never ends on a list with a cycle.

Task 3 · Code · Python

Write merge_two_lists(l1, l2) that merges two linked lists, each sorted in non-decreasing order, into one sorted list and returns its head. Relink the existing nodes (do not create new value nodes; a dummy node is fine). O(n + m) time, O(1) extra space. Example: 1 -> 2 -> 4 and 1 -> 3 -> 4 give 1 -> 1 -> 2 -> 3 -> 4 -> 4.

Task 4 · Code · Python

Write remove_nth_from_end(head, n) that removes the n-th node from the END of the list (n = 1 is the last node) and returns the head. 1 <= n <= length. Do it in ONE pass with two pointers n nodes apart, starting from a dummy node. Example: 1 -> 2 -> 3 -> 4 -> 5 with n = 2 becomes 1 -> 2 -> 3 -> 5.

Quick quiz

Question 1

1.With while fast and fast.next, which node does slow stop at for 10 → 20 → 30 → 40?

Question 2

2.What is the extra space of Floyd's cycle detection?

Question 3

3.After slow and fast meet inside a cycle, how do you find the first node of the cycle?

Question 4

4.Why compare slow is fast rather than slow.val == fast.val?

Question 5

5.When merging two sorted lists, one list runs out. What do you do with the other?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

With while fast and fast.next, which node does slow stop at for 10 → 20 → 30 → 40?

Answer: 30

For an even length this loop gives the second middle: slow moves to 20, then 30, while fast moves to 30, then off the end.

What is the extra space of Floyd's cycle detection?

Answer: O(1)

Only two pointers, compared with a set of visited nodes, which is O(n).

After slow and fast meet inside a cycle, how do you find the first node of the cycle?

Answer: Move one pointer back to the head and step both one node at a time until they meet

Because a = k·c − b, both pointers reach the cycle start after a steps.

Why compare slow is fast rather than slow.val == fast.val?

Answer: Two different nodes can hold the same value

Equal values do not mean the same node; [7, 7, 7] has no cycle.

When merging two sorted lists, one list runs out. What do you do with the other?

Answer: Attach its remaining part in one step: tail.next = a or b

The remaining part is already sorted and larger than everything merged so far.

What problem does a dummy node solve?Related

Answer: Deleting or inserting at the head no longer needs special code

With a node before the head, every real node has a predecessor; return dummy.next at the end.

From Linked lists

Why does push_front take Node **head?Related

Answer: To change the caller's head pointer

The function must update the caller's pointer.

From Singly linked lists

Merging sorted arrays of sizes n and m with two pointers costs…Related

Answer: O(n + m)

Each step moves one pointer forward, so there are n + m steps in total.

From Two pointers

You fixed all conflicts in two files. What is the next step?Related

Answer: git add the files, then git commit

Staging the files marks them resolved; the commit completes the merge. --abort would throw your work away.

From Branching, merging and conflicts

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. This is the last topic of “Stacks, queues and linked lists”. The module exam comes next.
  2. Module 4 exam: Stacks, queues and linked lists · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Recursion and backtracking. 61 topics left in the course before the final exam.

← Previous: Linked lists