2095. Delete the Middle Node of a Linked List
Medium · solved 2026-10-05 · Open on LeetCode
linked-listtwo-pointers
Time O(n) · Space O(1)
How to get there
Stop one node before the middle
1. Find what a deletion needs. A singly linked list removes a node by rewiring the node before it: before.next = before.next.next. The middle is at index n // 2, so the walk has to stop at index n // 2 − 1.
2. Walk it by hand and find where one pass gets stuck. On [1, 3, 4, 7, 1, 2, 6], n = 7, so the middle is index 3, the 7, and the walk must stop at index 2, the 4. But n is known only at the end of the list, and by then index 2 is behind, with no way back. Counting first and walking again takes two passes.
3. Find what to remember instead. The middle of the nodes seen so far. After k nodes it sits at index k // 2, so it moves one step for every two nodes the walk covers. When the walk reaches the end, that pointer is at the middle of the whole list.
4. Let two speeds do the counting. “One step for every two” is two pointers: fast moves two nodes per turn and slow one. Starting fast two nodes ahead keeps slow one node behind the middle, which is exactly where step 1 needs it.
5. Handle the list with no node before the middle. With one node, the middle is the head itself, and deleting it leaves an empty list: return None. Every longer list has a node before its middle.
6. Check the cost. One pass of about n/2 turns, and two pointers: O(n) time, O(1) space.
The reusable recipe: to reach a fixed fraction of a list’s length in one pass, move two pointers at speeds in that ratio. To delete a node, stop on the one before it.
Let the pointers' meanings write the loop
| Variable | Always means, at the top of the loop, after i turns |
|---|---|
slow |
the node at index i |
fast |
the node at index 2i + 2, or None once that index is past the end |
| Code | What happened | What it keeps true |
|---|---|---|
if not head.next: return None |
the list has one node, which is the middle | the empty list is the answer, and head.next.next below is safe |
slow, fast = head, head.next.next |
no turns yet, i = 0 | slow at index 0, fast at index 2 |
while fast and fast.next: |
both index 2i + 2 and 2i + 3 exist | there is room for fast to move two nodes |
slow = slow.next, fast = fast.next.next |
one more turn, i grows by 1 | slow gains 1 and fast gains 2 |
slow.next = slow.next.next |
the loop stopped | slow is right before the middle, so this unlinks it |
Where the loop stops depends on whether n is even or odd:
| n | fast stops at |
slow stops at |
|---|---|---|
| even | index n, which is None |
index n/2 − 1 |
| odd | index n − 1, the last node | index (n − 3)/2 |
Both are n // 2 − 1, the node before the middle.
The method carries over: write down where each pointer always is, make each turn the smallest move that keeps it there, then solve the stop condition for the index you need.
Alternatives compared
| Approach | Time | Space | How it works |
|---|---|---|---|
| Copy the nodes into a list | O(n) | O(n) | Store every node in an array, then set nodes[n // 2 - 1].next = nodes[n // 2].next. |
| Count, then walk again | O(n), two passes | O(1) | The first pass finds n; the second stops at index n // 2 − 1. |
One cursor, move the middle every other node (solution-1-original.py) |
O(n) | O(1) | cursor visits every node, and a parity counter moves mid_cursor on every second one, with prev_cursor one behind it. |
Fast and slow pointers (chosen, solution-2-clean.py) |
O(n) | O(1) | fast moves two nodes per turn and slow one, starting two apart, so slow stops on the node before the middle. |
The last two make the same single pass. On a 100,000-node list in CPython, measured on one machine, the original took 12.0 ms and fast and slow took 2.3 ms. The original runs n turns with a modulo and a counter in each, while fast and slow runs n/2 turns of two pointer moves.
solution-1-original.py
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def deleteMiddle(self, head: ListNode | None) -> ListNode | None:
# mid_cursor, cursor
# 0, 0: skip -> 0, 0
# 0, 1: move -> 1, 1
# 1, 2: skip -> 1, 2
# 1, 3: move -> 2, 3
# 2, 4: skip -> 2, 4
# 2, 5: move -> 3, 5
cursor, counter, mid_index, prev_cursor, mid_cursor = head, 0, 0, head, head
if not head.next:
return None
while cursor:
if counter % 2 != 0:
mid_index += 1
prev_cursor = mid_cursor
mid_cursor = mid_cursor.next
counter += 1
cursor = cursor.next
prev_cursor.next = mid_cursor.next
return headsolution-2-clean.py
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def deleteMiddle(self, head: ListNode | None) -> ListNode | None:
if not head.next:
return None
slow, fast = head, head.next.next
while fast and fast.next:
slow = slow.next
fast = fast.next.next
slow.next = slow.next.next
return headNotes
Write your approach, insights, and mistakes here. Any other file in this folder (e.g. solution.py, attempt-1.py) is rendered below as code.