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.
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
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
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 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} 41Solution 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 (prevents error when list has odd length)fast.nextchecks 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:
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
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
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)
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/AttributeErrorwhenfastitself becomesNone(happens with odd-length lists) - Using only
while fast:will cause an error when trying to accessfast.next.nextiffast.nextisNone(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 of the tree traversal order can be used to obtain elements in a binary search tree in sorted order?
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!