LeetCopilot Logo
LeetCopilot
LeetCode Pattern/Two Pointers/Two Pointers on Linked Lists: Common Pitfalls and Null Pointer Errors

Two Pointers on Linked Lists: Common Pitfalls and Null Pointer Errors

LeetCopilot Team
Dec 9, 2025
10 min read
Two PointersLinked ListNull PointerCommon MistakesDebugging
Linked list two pointers problems crash with null pointer exceptions more than any other pattern. Learn safe pointer advancement, null checks, and how to avoid the most common mistakes.

Your linked list solution looks perfect. You've implemented fast and slow pointers. You submit.

Runtime Error: NullPointerException

Or in Python: AttributeError: 'NoneType' object has no attribute 'next'

You check your code. Everything seems fine. But somewhere, somehow, you're trying to access .next on a null pointer.

Linked list two pointers problems have a unique challenge: unlike arrays where indices are always valid (until you go out of bounds), linked list pointers can become null at any time. One wrong assumption about fast.next or slow.next, and your code crashes.

This guide will teach you the safe patterns for linked list two pointers, the exact null checks you need, and how to avoid the most common pitfalls.

TL;DR

  • Always check fast and fast.next before advancing fast pointer by 2
  • Use while fast and fast.next: for fast/slow pattern
  • Check slow.next before accessing slow.next.next
  • Handle edge cases: empty list, single node, two nodes
  • Common mistake: Forgetting to check fast.next before fast.next.next

Why Linked Lists Are Tricky

Arrays vs Linked Lists

Arrays:

python
# Safe: indices are always valid within bounds
if left < len(nums):
    value = nums[left]  # Won't crash

Linked Lists:

python
# UNSAFE: current might be None
value = current.next  # Might crash!

The Null Pointer Problem

In linked lists:

  • Any node's .next can be None (end of list)
  • Accessing None.next crashes immediately
  • You must check before every access

The Safe Fast/Slow Pattern

The Standard Template

python
def hasCycle(head):
    slow = fast = head
    
    # Key: Check BOTH fast and fast.next
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        
        if slow == fast:
            return True
    
    return False

Why This Works

The condition while fast and fast.next: ensures:

  1. fast is not None → safe to access fast.next
  2. fast.next is not None → safe to access fast.next.next

What Happens Without Proper Checks

python
# WRONG: Missing fast.next check
def hasCycle(head):
    slow = fast = head
    
    while fast:  # Only checks fast!
        slow = slow.next
        fast = fast.next.next  # CRASH if fast.next is None!
        
        if slow == fast:
            return True
    
    return False

Example crash:

code
List: 1 -> 2 -> None

Iteration 1:
  slow = 1, fast = 1
  slow = slow.next = 2
  fast = fast.next.next
    fast.next = 2
    fast.next.next = 2.next = None ✓ (works)

Iteration 2:
  slow = 2, fast = None
  Loop continues (fast is None, but while fast: is True... wait, no)
  Actually, fast is None, so loop exits ✓

But if we had:
List: 1 -> None

Iteration 1:
  slow = 1, fast = 1
  slow = slow.next = None
  fast = fast.next.next
    fast.next = None
    fast.next.next = None.next ✗ CRASH!

Common Null Pointer Pitfalls

Pitfall 1: Not Checking `fast.next`

python
# WRONG
while fast:
    fast = fast.next.next  # Crashes if fast.next is None

Fix:

python
# CORRECT
while fast and fast.next:
    fast = fast.next.next

Pitfall 2: Accessing `.next.next` Without Checks

python
# WRONG
if slow.next.next:  # Crashes if slow.next is None!
    # ...

Fix:

python
# CORRECT
if slow.next and slow.next.next:
    # ...

Pitfall 3: Forgetting Edge Cases

python
# WRONG: Doesn't handle empty list
def middleNode(head):
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
    return slow  # Crashes if head is None!

Fix:

python
# CORRECT
def middleNode(head):
    if not head:
        return None
    
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
    return slow

Pitfall 4: Wrong Initialization

python
# WRONG: fast starts at head.next without checking
def hasCycle(head):
    slow = head
    fast = head.next  # Crashes if head is None!
    # ...

Fix:

python
# CORRECT
def hasCycle(head):
    if not head or not head.next:
        return False
    
    slow = head
    fast = head.next
    # ...

Safe Patterns for Common Problems

Pattern 1: Find Middle of Linked List

python
def middleNode(head):
    # Edge case: empty list
    if not head:
        return None
    
    slow = fast = head
    
    # Safe: checks both fast and fast.next
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
    
    return slow

Why it's safe:

  • Handles empty list (returns None)
  • Checks fast and fast.next before advancing
  • Works for odd and even length lists

Test cases:

code
Empty: None → None
Single: 1 → 1
Two: 1->2 → 2
Three: 1->2->3 → 2
Four: 1->2->3->4 → 3

Pattern 2: Detect Cycle

python
def hasCycle(head):
    slow = fast = head
    
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        
        if slow == fast:
            return True
    
    return False

Why it's safe:

  • No explicit edge case check needed (loop handles it)
  • If head is None, fast is None, loop doesn't run
  • If single node, fast.next is None, loop doesn't run

Pattern 3: Remove Nth Node From End

python
def removeNthFromEnd(head, n):
    # Use dummy node to handle edge cases
    dummy = ListNode(0)
    dummy.next = head
    
    slow = fast = dummy
    
    # Move fast n+1 steps ahead
    for _ in range(n + 1):
        if not fast:  # Check before advancing
            return head
        fast = fast.next
    
    # Move both until fast reaches end
    while fast:
        slow = slow.next
        fast = fast.next
    
    # Remove the node
    slow.next = slow.next.next
    
    return dummy.next

Why it's safe:

  • Dummy node handles removing head
  • Checks fast before advancing
  • Ensures slow.next exists before removing

Pattern 4: Palindrome Linked List

python
def isPalindrome(head):
    if not head or not head.next:
        return True
    
    # Find middle
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
    
    # Reverse second half
    prev = None
    while slow:
        next_node = slow.next
        slow.next = prev
        prev = slow
        slow = next_node
    
    # Compare
    left, right = head, prev
    while right:  # Only need to check right (shorter half)
        if left.val != right.val:
            return False
        left = left.next
        right = right.next
    
    return True

Why it's safe:

  • Edge case check at start
  • Safe middle-finding
  • Reversal checks slow before accessing .next
  • Comparison only checks right (guaranteed to be shorter or equal)

The Null Check Checklist

Before accessing any pointer property, verify:

✅ Single Pointer Access

python
# Before: current.next
if current:
    value = current.next  # Safe

✅ Double Pointer Access

python
# Before: current.next.next
if current and current.next:
    value = current.next.next  # Safe

✅ Fast Pointer (2 steps)

python
# Before: fast.next.next
if fast and fast.next:
    fast = fast.next.next  # Safe

✅ Slow Pointer (1 step)

python
# Before: slow.next
if slow:
    slow = slow.next  # Safe

Edge Cases to Always Test

Edge Case 1: Empty List

python
head = None

# Your code should handle this without crashing
result = yourFunction(None)

Edge Case 2: Single Node

python
head = ListNode(1)
head.next = None

# Fast pointer immediately becomes None

Edge Case 3: Two Nodes

python
head = ListNode(1)
head.next = ListNode(2)

# Fast pointer reaches end after one iteration

Edge Case 4: Cycle at Head

python
head = ListNode(1)
head.next = head  # Points to itself

# Should detect cycle immediately

Debugging Null Pointer Errors

When you get a null pointer error:

Step 1: Identify the Line

code
AttributeError: 'NoneType' object has no attribute 'next'
  File "solution.py", line 12, in hasCycle
    fast = fast.next.next

The error is on line 12: fast.next.next

Step 2: Add Debug Prints

python
while fast and fast.next:
    print(f"Before: slow={slow.val if slow else None}, fast={fast.val if fast else None}")
    slow = slow.next
    fast = fast.next.next
    print(f"After: slow={slow.val if slow else None}, fast={fast.val if fast else None}")

Step 3: Check the Condition

python
# Is your while condition correct?
while fast:  # WRONG: should be "fast and fast.next"
    fast = fast.next.next  # This can crash!

Step 4: Test Edge Cases

python
# Test with minimal inputs
test_cases = [
    None,  # Empty
    ListNode(1),  # Single
    ListNode(1, ListNode(2)),  # Two nodes
]

for test in test_cases:
    try:
        result = yourFunction(test)
        print(f"✓ Passed: {test}")
    except Exception as e:
        print(f"✗ Failed: {test}, Error: {e}")

Language-Specific Considerations

Python

python
# Python uses "None" for null
if node is None:  # Preferred
if not node:      # Also works

# Accessing None.next raises AttributeError

Java

java
// Java uses "null"
if (node == null) {
    // Handle null
}

// Accessing null.next throws NullPointerException

JavaScript

javascript
// JavaScript uses "null" or "undefined"
if (node === null || node === undefined) {
    // Handle null
}
if (!node) {  // Also works (checks both null and undefined)

// Accessing null.next throws TypeError

Practice Problems

To master safe linked list two pointers:

  1. Linked List Cycle (#141) - basic fast/slow with null checks
  2. Middle of Linked List (#876) - finding middle safely
  3. Remove Nth Node From End (#19) - careful pointer advancement
  4. Palindrome Linked List (#234) - multiple pointer operations
  5. Intersection of Two Linked Lists (#160) - two independent pointers

FAQ

Q: Why do I need to check both fast and fast.next?

A: Because you're advancing fast by 2 steps: fast.next.next. You need to ensure both fast.next and fast.next.next exist. Checking fast and fast.next guarantees this.

Q: Can I just use try/except to catch null pointer errors?

A: Technically yes, but it's bad practice. It's slower and makes your code harder to understand. Always check explicitly.

Q: What if I want fast to start one step ahead?

A: Check if head exists first:

python
if not head or not head.next:
    return False
slow = head
fast = head.next

Q: How do I know if my null checks are correct?

A: Test with edge cases: empty list, single node, two nodes. If all pass, your checks are likely correct.

Conclusion

Null pointer errors in linked list two pointers are preventable with systematic checks:

Key principles:

  1. Always check fast and fast.next before fast.next.next
  2. Check node before node.next
  3. Handle edge cases: empty, single node, two nodes
  4. Use while fast and fast.next: as your standard loop condition
  5. Test edge cases before submitting

The safe pattern:

python
slow = fast = head
while fast and fast.next:
    slow = slow.next
    fast = fast.next.next

Remember:

  • Arrays: indices are safe (until out of bounds)
  • Linked lists: pointers can be null anytime (must check)

Master these null checks, and you'll never crash on a linked list problem again. For more on two pointers, see the complete guide and fast/slow pointers.

Next time you write fast.next.next, you'll automatically check fast and fast.next first.

Want to Practice LeetCode Smarter?

LeetCopilot is a free browser extension that enhances your LeetCode practice with AI-powered hints, personalized study notes, and realistic mock interviews — all designed to accelerate your coding interview preparation.

Also compatible with Edge, Brave, and Opera

Related Tutorials