Problem
Given a singly linked list, remove the node at the exact middle position and return the modified list. The middle is the โn/2โ-th node using 0-based indexing โ so for a 7-node list the middle is at index 3, and for a 4-node list it is at index 2.
[1,3,4,7,1,2,6]
- Input: head = [1, 3, 4, 7, 1, 2, 6]
- Output: [1, 3, 4, 1, 2, 6]
- Explanation: The list has 7 nodes; โ7/2โ = 3, so the node at index 3 (value 7) is deleted.
If the list has exactly one node, the result is an empty list.
Intuition
The challenge is locating the middle in a single pass without knowing the length upfront. A fast pointer that moves twice as quickly as a slow pointer positions the slow pointer exactly at the middle when the fast pointer exhausts the list โ the classic "tortoise and hare" pattern. We also need the node just before the middle so we can unlink it, which a trailing prev pointer provides.
Solution โ Fast and Slow Pointers
Use three pointers: fast advances two nodes per step, slow advances one, and prev always trails one step behind slow. When the loop exits, slow is at the middle and prev is the node just before it. A dummy head handles the case where the middle is the original head (n = 1).
- Create a dummy node pointing to
headso deletions near the front work uniformly. - Initialize
prev = dummy,slow = head,fast = head. - While
fastandfast.nextare both non-null: setprev = slow, advanceslowone step, advancefasttwo steps. - When the loop ends,
slowis at the middle node. - Unlink it:
prev.next = slow.next. - Return
dummy.nextas the updated head.
1def deleteMiddle(self, head: Optional[ListNode]) -> Optional[ListNode]:
2 dummy = ListNode(0, head)
3 prev = dummy
4 slow = head
5 fast = head
6
7 while fast and fast.next:
8 prev = slow # keep prev one step behind slow
9 slow = slow.next
10 fast = fast.next.next
11
12 prev.next = slow.next # unlink the middle node
13 return dummy.nextTime: O(n) โ one complete pass through the list.
Space: O(1) โ only a fixed number of pointers regardless of list length.
Complexity Summary
| Approach | Time | Space | When to use |
|---|---|---|---|
| Fast & Slow Pointers | O(n) | O(1) | Always โ canonical one-pass solution with no length pre-computation |
Common Mistakes
- Omitting the dummy node: When n = 1, the middle is the head itself; without a dummy predecessor you cannot delete the head and return
nullcleanly.dummy.nextabsorbs this case without any special branch. - Reversing the
prev/slowupdate order:prev = slowmust happen beforeslow = slow.next. Writing them in reverse order meansprevalways equals the newslow, making it trail by zero steps instead of one. - Starting
fastathead.next: This shifts the fast pointer's headstart and causesslowto land one node past the true middle on even-length lists. - Returning
headinstead ofdummy.next: When the original head is deleted (n = 1), the variableheadstill points to the removed node. Onlydummy.nextreflects the updated list. - Off-by-one on even-length lists: For n = 4, โ4/2โ = 2, which is the third node (0-indexed). Tracing through [1,2,3,4] by hand before coding catches this before it becomes a wrong submission.
Related Problems
middle-of-the-linked-listโ identical fast/slow pointer setup, except you return the middle node instead of deleting it; a perfect warm-upremove-nth-node-from-end-of-listโ fast/slow variant where fast starts N steps ahead to reach the Nth-from-last nodepalindrome-linked-listโ also locates the middle with fast/slow before reversing the second half to check symmetryreorder-listโ splits the list at the middle using fast/slow, reverses the back half, then interleaves the two halvesodd-even-linked-listโ pointer manipulation that regrouping nodes by index parity, reinforcing multi-pointer linked list technique