876. Middle of the Linked List
Problem Description
You are given the head of a singly linked list. Your task is to find and return the middle node of this linked list.
The middle node is defined as follows:
- If the linked list has an odd number of nodes, return the node at the exact center position
- If the linked list has an even number of nodes, there will be two middle nodes - return the second middle node
For example:
- If the linked list is
1 -> 2 -> 3 -> 4 -> 5
, the middle node is3
(the third node) - If the linked list is
1 -> 2 -> 3 -> 4 -> 5 -> 6
, there are two middle nodes (3
and4
), so you should return4
(the second middle node)
The solution uses the two-pointer technique (also known as the tortoise and hare algorithm):
- Initialize two pointers,
slow
andfast
, both starting at thehead
- Move
slow
one step at a time (slow = slow.next
) - Move
fast
two steps at a time (fast = fast.next.next
) - Continue this process while
fast
andfast.next
are notNone
- When
fast
reaches the end,slow
will be at the middle node
This works because fast
moves twice as fast as slow
. When fast
reaches the end of the list, slow
will have traveled exactly half the distance, positioning it at the middle node. For even-length lists, this naturally gives us the second middle node as required.
Intuition
The key insight comes from thinking about the relationship between distance and speed. Imagine two runners on a track - one runs twice as fast as the other. When the faster runner completes the full track, where would the slower runner be? Exactly at the halfway point!
This same principle applies to traversing a linked list. If we have two pointers moving through the list at different speeds - one moving one node at a time and another moving two nodes at a time - when the faster pointer reaches the end, the slower pointer will be at the middle.
Why does this work mathematically? Let's say the linked list has n
nodes:
- The
slow
pointer moves 1 step per iteration - The
fast
pointer moves 2 steps per iteration - When
fast
has traversedn
nodes (reaching the end), it tookn/2
iterations - In those same
n/2
iterations,slow
movedn/2
nodes, placing it exactly at the middle
For odd-length lists (like 5 nodes), slow
ends up at position ⌊5/2⌋ + 1 = 3
(the exact middle).
For even-length lists (like 6 nodes), slow
ends up at position 6/2 = 3
, but since we count from position 0, this gives us the 4th node, which is the second middle node - exactly what we want!
The beauty of this approach is that we don't need to know the length of the list beforehand. We don't need to traverse it once to count nodes and then traverse again to find the middle. Everything happens in a single pass, making this an elegant O(n)
time solution with O(1)
space complexity.
Learn more about Linked List and Two Pointers patterns.
Solution Approach
The implementation uses the fast and slow pointer technique (Floyd's Tortoise and Hare algorithm) to find the middle node in a single pass.
Algorithm Steps:
-
Initialize two pointers: Both
slow
andfast
start at thehead
of the linked listslow = fast = head
-
Traverse the linked list with different speeds:
- The loop continues while both
fast
andfast.next
exist - This condition ensures we don't get a null pointer error when
fast
tries to jump two nodes
while fast and fast.next:
- The loop continues while both
-
Move the pointers:
slow
moves one step forward:slow = slow.next
fast
moves two steps forward:fast = fast.next.next
- Python allows simultaneous assignment, so we can write:
slow, fast = slow.next, fast.next.next
-
Return the middle node: When the loop exits,
slow
points to the middle nodereturn slow
Why the loop condition works:
fast
checks if the fast pointer itself exists (prevents error when list has odd length)fast.next
checks if fast can make another two-step jump (prevents error when list has even length)- When either becomes
None
, we've reached or passed the end of the list
Example walkthrough:
-
For list
1->2->3->4->5
:- Initially:
slow
at 1,fast
at 1 - Iteration 1:
slow
at 2,fast
at 3 - Iteration 2:
slow
at 3,fast
at 5 - Loop ends (fast.next is None), return node 3
- Initially:
-
For list
1->2->3->4
:- Initially:
slow
at 1,fast
at 1 - Iteration 1:
slow
at 2,fast
at 3 - Iteration 2:
slow
at 3,fast
is None - Loop ends, return node 3 (the second middle node)
- Initially:
Time Complexity: O(n)
where n is the number of nodes - we traverse at most n/2 iterations
Space Complexity: O(1)
- only using two pointers regardless of input size
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through finding the middle node of the linked list: 1 -> 2 -> 3 -> 4 -> 5
Initial Setup:
- Both
slow
andfast
pointers start at the head (node 1) - List visualization:
[1] -> 2 -> 3 -> 4 -> 5
slow
points to node 1fast
points to node 1
Iteration 1:
- Check condition:
fast
(node 1) exists ✓ andfast.next
(node 2) exists ✓ - Move pointers:
slow
moves one step:slow = slow.next
→ now at node 2fast
moves two steps:fast = fast.next.next
→ now at node 3
- List visualization:
1 -> [2] -> (3) -> 4 -> 5
[2]
= slow pointer position(3)
= fast pointer position
Iteration 2:
- Check condition:
fast
(node 3) exists ✓ andfast.next
(node 4) exists ✓ - Move pointers:
slow
moves one step: now at node 3fast
moves two steps: now at node 5
- List visualization:
1 -> 2 -> [3] -> 4 -> (5)
[3]
= slow pointer position(5)
= fast pointer position
Iteration 3:
- Check condition:
fast
(node 5) exists ✓ butfast.next
is None ✗ - Loop terminates
- Return
slow
which points to node 3
Result: Node 3 is correctly identified as the middle node of the 5-node list.
For an even-length list like 1 -> 2 -> 3 -> 4
, the algorithm would proceed:
- Start:
slow
andfast
at node 1 - After iteration 1:
slow
at node 2,fast
at node 3 - After iteration 2:
slow
at node 3,fast
becomes None (moved past node 4) - Returns node 3, which is the second middle node (nodes 2 and 3 are both "middle", we return the second one)
Solution Implementation
1# Definition for singly-linked list.
2# class ListNode:
3# def __init__(self, val=0, next=None):
4# self.val = val
5# self.next = next
6
7class Solution:
8 def middleNode(self, head: ListNode) -> ListNode:
9 """
10 Find the middle node of a linked list using the two-pointer technique.
11
12 Args:
13 head: The head node of the linked list
14
15 Returns:
16 The middle node of the linked list. If the list has an even number
17 of nodes, returns the second middle node.
18 """
19 # Initialize both pointers to start at the head
20 slow_pointer = head
21 fast_pointer = head
22
23 # Move fast pointer twice as fast as slow pointer
24 # When fast pointer reaches the end, slow pointer will be at the middle
25 while fast_pointer and fast_pointer.next:
26 # Move slow pointer one step forward
27 slow_pointer = slow_pointer.next
28 # Move fast pointer two steps forward
29 fast_pointer = fast_pointer.next.next
30
31 # Return the middle node (where slow pointer ended up)
32 return slow_pointer
33
1/**
2 * Definition for singly-linked list.
3 * public class ListNode {
4 * int val;
5 * ListNode next;
6 * ListNode() {}
7 * ListNode(int val) { this.val = val; }
8 * ListNode(int val, ListNode next) { this.val = val; this.next = next; }
9 * }
10 */
11class Solution {
12 /**
13 * Finds the middle node of a linked list.
14 * If the list has an even number of nodes, returns the second middle node.
15 * Uses the two-pointer technique (slow and fast pointers).
16 *
17 * @param head The head of the linked list
18 * @return The middle node of the linked list
19 */
20 public ListNode middleNode(ListNode head) {
21 // Initialize two pointers at the head of the list
22 // slowPointer moves one step at a time
23 // fastPointer moves two steps at a time
24 ListNode slowPointer = head;
25 ListNode fastPointer = head;
26
27 // Traverse the list until fastPointer reaches the end
28 // When fastPointer reaches the end, slowPointer will be at the middle
29 while (fastPointer != null && fastPointer.next != null) {
30 // Move slowPointer one step forward
31 slowPointer = slowPointer.next;
32 // Move fastPointer two steps forward
33 fastPointer = fastPointer.next.next;
34 }
35
36 // Return the middle node (where slowPointer is positioned)
37 return slowPointer;
38 }
39}
40
1/**
2 * Definition for singly-linked list.
3 * struct ListNode {
4 * int val;
5 * ListNode *next;
6 * ListNode() : val(0), next(nullptr) {}
7 * ListNode(int x) : val(x), next(nullptr) {}
8 * ListNode(int x, ListNode *next) : val(x), next(next) {}
9 * };
10 */
11class Solution {
12public:
13 /**
14 * Find the middle node of a linked list.
15 * Uses the two-pointer technique (slow and fast pointers).
16 *
17 * @param head The head of the linked list
18 * @return The middle node of the linked list
19 * If the list has an even number of nodes, returns the second middle node
20 */
21 ListNode* middleNode(ListNode* head) {
22 // Initialize two pointers at the head of the list
23 ListNode* slowPointer = head;
24 ListNode* fastPointer = head;
25
26 // Move fast pointer twice as fast as slow pointer
27 // When fast pointer reaches the end, slow pointer will be at the middle
28 while (fastPointer != nullptr && fastPointer->next != nullptr) {
29 // Move slow pointer one step forward
30 slowPointer = slowPointer->next;
31
32 // Move fast pointer two steps forward
33 fastPointer = fastPointer->next->next;
34 }
35
36 // Return the middle node (pointed by slow pointer)
37 return slowPointer;
38 }
39};
40
1/**
2 * Definition for singly-linked list node
3 */
4interface ListNode {
5 val: number;
6 next: ListNode | null;
7}
8
9/**
10 * Finds the middle node of a linked list using the two-pointer technique
11 *
12 * @param head - The head node of the linked list
13 * @returns The middle node of the linked list, or the second middle node if the list has an even number of nodes
14 *
15 * Time Complexity: O(n) where n is the number of nodes in the linked list
16 * Space Complexity: O(1) as we only use two pointers
17 *
18 * Algorithm:
19 * - Uses fast and slow pointers (Floyd's Tortoise and Hare algorithm)
20 * - Fast pointer moves two steps at a time
21 * - Slow pointer moves one step at a time
22 * - When fast pointer reaches the end, slow pointer will be at the middle
23 */
24function middleNode(head: ListNode | null): ListNode | null {
25 // Initialize both pointers at the head of the list
26 let fastPointer: ListNode | null = head;
27 let slowPointer: ListNode | null = head;
28
29 // Traverse the list until fast pointer reaches the end
30 // Check both fast pointer and its next node to avoid null reference
31 while (fastPointer !== null && fastPointer.next !== null) {
32 // Move fast pointer two steps forward
33 fastPointer = fastPointer.next.next;
34 // Move slow pointer one step forward
35 slowPointer = slowPointer.next!;
36 }
37
38 // Return the slow pointer which is now at the middle node
39 return slowPointer;
40}
41
Time and Space Complexity
Time Complexity: O(n)
where n
is the number of nodes in the linked list. The algorithm uses two pointers - a slow pointer that moves one step at a time and a fast pointer that moves two steps at a time. The fast pointer will reach the end of the list after traversing approximately n/2
iterations (since it moves twice as fast), at which point the loop terminates. Therefore, the slow pointer visits approximately n/2
nodes and the fast pointer visits approximately n
nodes, resulting in a total time complexity of O(n)
.
Space Complexity: O(1)
- The algorithm only uses two pointer variables (slow
and fast
) regardless of the size of the input linked list. No additional data structures or recursive calls are made that would scale with the input size, making the space complexity constant.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Loop Condition
A common mistake is using only while fast.next:
or while fast:
as the loop condition instead of while fast and fast.next:
.
Why it's wrong:
- Using only
while fast.next:
will cause aNullPointerException/AttributeError
whenfast
itself becomesNone
(happens with odd-length lists) - Using only
while fast:
will cause an error when trying to accessfast.next.next
iffast.next
isNone
(happens with even-length lists)
Incorrect implementation:
# This will fail for odd-length lists
while fast.next: # Error when fast is None
slow = slow.next
fast = fast.next.next
Correct implementation:
# Check both fast and fast.next
while fast and fast.next:
slow = slow.next
fast = fast.next.next
2. Moving Pointers in Wrong Order
Another pitfall is updating the pointers in the wrong sequence when not using simultaneous assignment.
Why it's wrong:
If you update fast
first using sequential assignments, you lose the reference needed for updating slow
.
Incorrect implementation:
while fast and fast.next:
fast = fast.next.next # Wrong: updating fast first
slow = slow.next # This still works but can lead to confusion
Better implementations:
# Option 1: Update slow first
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# Option 2: Use simultaneous assignment (Python feature)
while fast and fast.next:
slow, fast = slow.next, fast.next.next
3. Off-by-One Error for Even-Length Lists
Some might mistakenly think they need special handling for even-length lists to return the second middle node.
Why it's wrong: The algorithm naturally returns the second middle node for even-length lists. Adding extra logic is unnecessary and can introduce bugs.
Incorrect approach:
# Unnecessary complexity
def middleNode(self, head):
# Count nodes first
count = 0
current = head
while current:
count += 1
current = current.next
# Find middle based on count
middle_index = count // 2
current = head
for _ in range(middle_index):
current = current.next
return current
Correct approach: The two-pointer technique automatically handles both odd and even cases without special logic.
4. Not Handling Edge Cases
While the algorithm handles most cases well, ensure you consider:
- Single node list (works correctly as-is)
- Two node list (returns the second node correctly)
The beauty of the while fast and fast.next:
condition is that it handles all these edge cases without additional checks.
Which technique can we use to find the middle of a linked list?
Recommended Readings
Linked List Cycle Given a linked list with potentially a loop determine whether the linked list from the first node contains a cycle in it For bonus points do this with constant space Parameters nodes The first node of a linked list with potentially a loop Result Whether there is a loop contained
Tech Interview Pattern Two Pointers Introduction If you prefer videos here's a super quick introduction to Two Pointers div class responsive iframe iframe src https www youtube com embed xZ4AfXHQ1VQ title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture allowfullscreen iframe div Two pointers is a common interview
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!