You are given the head
of a linked list. Delete the middle node, and return the head
of the modified linked list.
The middle node of a linked list of size n
is the ⌊n / 2⌋th
node from the start using 0-based indexing, where ⌊x⌋
denotes the largest integer less than or equal to x
.
n
= 1
, 2
, 3
, 4
, and 5
, the middle nodes are 0
, 1
, 1
, 2
, and 2
, respectively.
Example 1:
Input: head = [1,3,4,7,1,2,6] Output: [1,3,4,1,2,6] Explanation: The above figure represents the given linked list. The indices of the nodes are written below. Since n = 7, node 3 with value 7 is the middle node, which is marked in red. We return the new list after removing this node.
Example 2:
Input: head = [1,2,3,4] Output: [1,2,4] Explanation: The above figure represents the given linked list. For n = 4, node 2 with value 3 is the middle node, which is marked in red.
Example 3:
Input: head = [2,1] Output: [2] Explanation: The above figure represents the given linked list. For n = 2, node 1 with value 1 is the middle node, which is marked in red. Node 0 with value 2 is the only node remaining after removing node 1.
Constraints:
[1, 105]
.1 <= Node.val <= 105
This problem requires deleting the middle node from a singly linked list. The "middle" node is defined as the floor(n/2)th node, where n is the number of nodes in the list. For example, in a list of 7 nodes, the middle node is the 3rd node (index 3).
The most efficient approach uses the fast and slow pointer technique. This technique leverages the relative speeds of two pointers traversing the list to identify the middle element.
Initialization: We start with a dummy node prepended to the head. This simplifies handling edge cases (empty list or single-node list). A slow pointer (slow
) starts at the dummy node, and a fast pointer (fast
) starts at the head.
Iteration: The fast
pointer moves twice as fast as the slow
pointer. In each iteration:
slow
moves one step forward (slow = slow.next
).fast
moves two steps forward (fast = fast.next.next
).Termination: The loop continues until fast
reaches the end of the list (i.e., fast
or fast.next
is null). At this point, slow
will be pointing to the node before the middle node.
Deletion: We delete the middle node by updating the next
pointer of the slow
node to skip over the middle node: slow.next = slow.next.next
.
Return: Finally, we return the next
pointer of the dummy node (which is the head of the modified list).
# 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]:
dummy = ListNode(next=head) # Dummy node for easy handling of edge cases
slow, fast = dummy, head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
slow.next = slow.next.next # Delete the middle node
return dummy.next # Return the head of the modified list
The code in other languages (Java, C++, Go, TypeScript) follows the same algorithm, just adapting the syntax to the respective language. The core logic remains identical, ensuring the same time and space complexity. The dummy node is crucial for handling edge cases elegantly without needing separate checks for empty or single-node lists.