Facebook Pixel

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 is 3 (the third node)
  • If the linked list is 1 -> 2 -> 3 -> 4 -> 5 -> 6, there are two middle nodes (3 and 4), so you should return 4 (the second middle node)

The solution uses the two-pointer technique (also known as the tortoise and hare algorithm):

  • Initialize two pointers, slow and fast, both starting at the head
  • 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 and fast.next are not None
  • 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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 traversed n nodes (reaching the end), it took n/2 iterations
  • In those same n/2 iterations, slow moved n/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:

  1. Initialize two pointers: Both slow and fast start at the head of the linked list

    slow = fast = head
  2. Traverse the linked list with different speeds:

    • The loop continues while both fast and fast.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:
  3. 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
  4. Return the middle node: When the loop exits, slow points to the middle node

    return 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
  • 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)

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 Evaluator

Example Walkthrough

Let's walk through finding the middle node of the linked list: 1 -> 2 -> 3 -> 4 -> 5

Initial Setup:

  • Both slow and fast pointers start at the head (node 1)
  • List visualization: [1] -> 2 -> 3 -> 4 -> 5
    • slow points to node 1
    • fast points to node 1

Iteration 1:

  • Check condition: fast (node 1) exists ✓ and fast.next (node 2) exists ✓
  • Move pointers:
    • slow moves one step: slow = slow.next → now at node 2
    • fast 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 ✓ and fast.next (node 4) exists ✓
  • Move pointers:
    • slow moves one step: now at node 3
    • fast 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 ✓ but fast.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 and fast 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 a NullPointerException/AttributeError when fast itself becomes None (happens with odd-length lists)
  • Using only while fast: will cause an error when trying to access fast.next.next if fast.next is None (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.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which technique can we use to find the middle of a linked list?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More