876. Middle of the Linked List - Explanation
Description
You are given the head of a singly linked list, return the middle node of the linked list.
If there are two middle nodes, return the second middle node.
Example 1:
Input: head = [1,2,3,4,5]
Output: [3,4,5]Example 2:
Input: head = [1,2,3,4,5,6]
Output: [4,5,6]Constraints:
1 <= The length of the list <= 1001 <= Node.val <= 100
Topics
Prerequisites
Before attempting this problem, you should be comfortable with:
- Linked Lists - Understanding node structure with value and next pointer, and how to traverse a linked list
- Two Pointers (Fast & Slow) - The technique of using two pointers moving at different speeds to find middle elements or detect cycles
- Arrays - Using dynamic arrays to store elements for indexed access
1. Convert To Array
Intuition
Linked lists do not support random access, so finding the middle node directly is not straightforward. By storing all nodes in an array, we gain index-based access. Once we have the array, the middle node is simply at index length / 2.
Algorithm
- Traverse the linked list and store each node in an array.
- Calculate the middle index as
length / 2. - Return the node at the middle index.
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]:
cur = head
arr = []
while cur:
arr.append(cur)
cur = cur.next
return arr[len(arr) // 2]Time & Space Complexity
- Time complexity:
- Space complexity:
2. Find Length of the List
Intuition
We can avoid storing all nodes by first counting the total number of nodes, then making a second pass to reach the middle. This uses constant extra space since we only store the count and a pointer.
Algorithm
- Traverse the list once to count the total number of nodes
n. - Calculate the middle position as
n / 2. - Traverse the list again, moving forward
n / 2steps from the head. - Return the node at that position.
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]:
n, cur = 0, head
while cur:
cur = cur.next
n += 1
n //= 2
cur = head
while n:
n -= 1
cur = cur.next
return curTime & Space Complexity
- Time complexity:
- Space complexity: extra space.
3. Fast & Slow Pointers
Intuition
The fast and slow pointer technique finds the middle in a single pass. The slow pointer moves one step at a time, while the fast pointer moves two steps. When the fast pointer reaches the end, the slow pointer will be at the middle. This works because the fast pointer covers twice the distance in the same number of iterations.
Algorithm
- Initialize both
slowandfastpointers at the head. - While
fastis notnullandfast.nextis notnull:- Move
slowone step forward. - Move
fasttwo steps forward.
- Move
- Return
slow, which now points to the middle node.
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]:
slow, fast = head, head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slowTime & Space Complexity
- Time complexity:
- Space complexity: extra space.
Common Pitfalls
Incorrect Loop Condition for Fast Pointer
When using the fast and slow pointer technique, the loop condition must check both fast and fast.next before advancing. Checking only fast can cause a null pointer exception when trying to access fast.next.next. The correct condition is while (fast != null && fast.next != null).
Off-by-One Error with Even-Length Lists
For lists with an even number of nodes, there are two middle nodes. The problem typically asks for the second middle node, but some implementations accidentally return the first. Ensure your pointer movement logic returns the correct middle based on the problem requirements.
Sign in to join the discussion