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 (3and4), 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,
slowandfast, both starting at thehead - Move
slowone step at a time (slow = slow.next) - Move
fasttwo steps at a time (fast = fast.next.next) - Continue this process while
fastandfast.nextare notNone - When
fastreaches the end,slowwill 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.
How We Pick the Algorithm
Why Linked List Two Pointers?
This problem maps to Linked List Two Pointers through a short path in the full flowchart.
A fast pointer moves two steps for every one of the slow pointer, so when the fast pointer reaches the end the slow pointer sits at the middle.
Open in FlowchartIntuition
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
slowpointer moves 1 step per iteration - The
fastpointer moves 2 steps per iteration - When
fasthas traversednnodes (reaching the end), it tookn/2iterations - In those same
n/2iterations,slowmovedn/2nodes, placing it exactly at the middle
In general, slow ends at 0-based index ⌊n/2⌋. For odd-length lists (like 5 nodes), that is index 2, the 3rd node (the exact middle).
For even-length lists (like 6 nodes), that is index 3, the 4th node, which is the second middle node as required.
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.
Pattern 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
slowandfaststart at theheadof the linked listslow = fast = head -
Traverse the linked list with different speeds:
- The loop continues while both
fastandfast.nextexist - This condition ensures we don't get a null pointer error when
fasttries to jump two nodes
while fast and fast.next: - The loop continues while both
-
Move the pointers:
slowmoves one step forward:slow = slow.nextfastmoves 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,
slowpoints to the middle nodereturn slow
Why the loop condition works:
fastchecks if the fast pointer itself exists (for an even-length list,fastbecomesNoneafter its last jump)fast.nextchecks if fast can make another two-step jump (for an odd-length list,faststops on the last node, wherefast.nextisNone)- When either becomes
None, we've reached or passed the end of the list
Example walkthrough:
-
For list
1->2->3->4->5:- Initially:
slowat 1,fastat 1 - Iteration 1:
slowat 2,fastat 3 - Iteration 2:
slowat 3,fastat 5 - Loop ends (fast.next is None), return node 3
- Initially:
-
For list
1->2->3->4:- Initially:
slowat 1,fastat 1 - Iteration 1:
slowat 2,fastat 3 - Iteration 2:
slowat 3,fastis 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
Example Walkthrough
Let's walk through finding the middle node of the linked list: 1 -> 2 -> 3 -> 4 -> 5
Initial Setup:
- Both
slowandfastpointers start at the head (node 1) - List visualization:
[1] -> 2 -> 3 -> 4 -> 5slowpoints to node 1fastpoints to node 1
Iteration 1:
- Check condition:
fast(node 1) exists ✓ andfast.next(node 2) exists ✓ - Move pointers:
slowmoves one step:slow = slow.next→ now at node 2fastmoves 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:
slowmoves one step: now at node 3fastmoves 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.nextis None ✗ - Loop terminates
- Return
slowwhich 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:
slowandfastat node 1 - After iteration 1:
slowat node 2,fastat node 3 - After iteration 2:
slowat node 3,fastbecomes 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
331/**
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}
401/**
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};
401/**
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}
41Time 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.
Pattern 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/AttributeErrorwhenfastitself becomesNone(happens with even-length lists) - Using only
while fast:will cause an error when trying to accessfast.next.nextiffast.nextisNone(happens with odd-length lists)
Incorrect implementation:
# This will fail for even-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. Order of the Pointer Updates
Updating fast before slow is sometimes flagged as a mistake, but it is not one here. The two updates do not depend on each other: slow = slow.next reads only slow, and fast = fast.next.next reads only fast.
Both orders work:
# Option 1: Update slow first
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# Option 2: Update fast first (same result)
while fast and fast.next:
fast = fast.next.next
slow = slow.next
# Option 3: Use simultaneous assignment (Python feature)
while fast and fast.next:
slow, fast = slow.next, fast.next.next
The order matters only if one update reads the other pointer, for example writing slow = fast.next by mistake.
3. Adding Special Handling for Even-Length Lists
Some might think they need extra logic for even-length lists to return the second middle node. The two-pointer loop already returns the second middle node, so no extra case is needed.
A counting approach is a correct alternative, but it walks the list twice:
# Correct, but uses two passes
def middleNode(self, head):
# Count nodes first
count = 0
current = head
while current:
count += 1
current = current.next
# Index count // 2 is the middle (the second middle for even lengths)
middle_index = count // 2
current = head
for _ in range(middle_index):
current = current.next
return current
Comparison: The two-pointer technique finds the same node in a single pass and handles both odd and even lengths with the same loop.
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.
Ready to land your dream job?
Unlock your dream job with a 5-minute quiz for a personalized study roadmap!
Get My RoadmapWhat are the two properties the problem needs to have for dynamic programming to be applicable? (Select 2)
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
Two Pointers Technique Explained If you prefer videos here's a super quick introduction to Two Pointers div class responsive iframe iframe width 560 height 315 src https www youtube nocookie com embed rQhNcycbf8w si lE7qtd1h_JSQwGpW title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share
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!