Loading problems...
TL;DR: Use the Two Pointer technique (Fast and Slow) to traverse the list in a single pass, locating the node immediately preceding the middle node to adjust its next pointer.
You are given the head of a linked list and tasked with deleting the middle node. The problem defines the middle node based on the size n of the list: it is the ⌊n / 2⌋-th node (using 0-based indexing). After removing the node, return the head of the modified list.
This is a popular interview question, often referred to as LeetCode 2095, that tests your ability to manipulate pointers efficiently without using extra space or multiple passes.
The naive approach relies on a two-pass strategy. Since we cannot determine the middle index without knowing the total length of the list, we must first count the nodes.
n.target = floor(n / 2).head, stopping at the node just before the target index (target - 1).next pointer of the predecessor node to skip the middle node.Pseudo-code:
textlength = 0 current = head while current is not null: length++ current = current.next target_index = length / 2 current = head for i from 0 to target_index - 1: current = current.next current.next = current.next.next return head
Complexity Analysis:
The core insight is to use two pointers moving at different speeds to simulate finding the middle without knowing the length n beforehand.
If we have a Slow Pointer moving 1 step at a time and a Fast Pointer moving 2 steps at a time, the Fast Pointer covers distance at twice the rate of the Slow Pointer.
n), the Slow Pointer will be exactly at the middle (index n/2).fast is always the distance covered by slow.The Deletion Constraint: To delete a node in a singly linked list, we require access to its predecessor (the node immediately before it). If the Slow Pointer lands directly on the middle node, we cannot easily delete it because we don't have a reference to the previous node.
To solve this, we can modify the initialization. Instead of starting both pointers at head, we can look ahead. Alternatively, we can track a previous pointer. The most elegant modification for this specific problem is to ensure the slow pointer stops one node before the middle. We achieve this by letting the fast pointer lead significantly or by skipping the first step for the slow pointer relative to the fast pointer's check.
Visual Description:
Imagine the list as a linear track. We initialize slow at the head and fast two steps ahead (or check two steps ahead). As the algorithm progresses, the fast pointer rapidly approaches the null terminator. The moment fast (or fast.next) hits null, the slow pointer is positioned exactly at the node prior to the one we need to delete. This allows us to perform slow.next = slow.next.next immediately.

Consider the input head = [1, 3, 4, 7, 1, 2, 6]. Length is 7. Middle node is 7 (index 3).
slow points to 1 (Index 0).fast points to 4 (Index 2).fast (Index 2) and fast.next (Index 3) are valid.slow moves to 3 (Index 1).fast moves to 1 (Index 4).fast (Index 4) and fast.next (Index 5) are valid.slow moves to 4 (Index 2).fast moves to 6 (Index 6).fast is at Index 6. fast.next is null. Loop terminates.slow is at node 4 (Index 2). The middle node is 7 (Index 3).slow.next = slow.next.next.4 now points to 1 (Index 4), skipping 7.[1, 3, 4, 1, 2, 6].The correctness relies on the arithmetic series of the pointers. Let be the number of nodes.
The fast pointer moves 2 steps for every 1 step of slow.
Normally, slow travels steps when fast travels steps.
By initializing fast at index 2 (conceptually head.next.next), we effectively simulate a race where fast has already traveled 2 units.
When fast reaches the end (), slow has traveled .
Index is exactly the predecessor of the middle node . This logic holds for both odd and even lengths due to integer division behavior.
cpp/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */ class Solution { public: ListNode* deleteMiddle(ListNode* head) { // Edge case: If list has only 1 node, return nullptr if (head == nullptr || head->next == nullptr) { return nullptr; } ListNode* slow = head; // Start fast two steps ahead to ensure slow stops at the predecessor ListNode* fast = head->next->next; while (fast != nullptr && fast->next != nullptr) { slow = slow->next; fast = fast->next->next; } // Delete the middle node ListNode* toDelete = slow->next; slow->next = slow->next->next; delete toDelete; // Optional: Free memory in C++ return head; } };
java/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val = val; } * ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } */ class Solution { public ListNode deleteMiddle(ListNode head) { // Edge case: If list has only 1 node, return null if (head == null || head.next == null) { return null; } ListNode slow = head; // Start fast two steps ahead so slow lands on the node BEFORE middle ListNode fast = head.next.next; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; } // Skip the middle node slow.next = slow.next.next; return head; } }
python# 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: Optional[ListNode]) -> Optional[ListNode]: # Edge case: If list has only 1 node, return None if not head or not head.next: return None slow = head # Start fast two steps ahead fast = head.next.next while fast and fast.next: slow = slow.next fast = fast.next.next # slow is now at the predecessor of the middle node slow.next = slow.next.next return head
fast pointer iterates through roughly steps, making the operation linear with respect to the number of nodes.slow and fast) for storage, regardless of the input size. No auxiliary data structures are used.The Two Pointer pattern used here is fundamental for linked list problems. Similar logic applies to:
fast.next.next without checking if fast or fast.next is null.slow and fast logic works generically for size 1. If initialized with head.next.next, a size-1 list causes an immediate crash or logic error.if head.next is None at the start.slow and fast at head without tracking a previous pointer.slow lands on the middle node. In a singly linked list, you cannot delete a node if you are standing on it (unless you copy values, which is hacky). You need the node before it.while fast != slow) leading to infinite loops if cycle detection logic is misapplied to a linear problem.fast reaching null.