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.nextbefore advancing fast pointer by 2 - Use
while fast and fast.next:for fast/slow pattern - Check
slow.nextbefore accessingslow.next.next - Handle edge cases: empty list, single node, two nodes
- Common mistake: Forgetting to check
fast.nextbeforefast.next.next
Why Linked Lists Are Tricky
Arrays vs Linked Lists
Arrays:
# Safe: indices are always valid within bounds
if left < len(nums):
value = nums[left] # Won't crashLinked Lists:
# UNSAFE: current might be None
value = current.next # Might crash!The Null Pointer Problem
In linked lists:
- Any node's
.nextcan beNone(end of list) - Accessing
None.nextcrashes immediately - You must check before every access
The Safe Fast/Slow Pattern
The Standard Template
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 FalseWhy This Works
The condition while fast and fast.next: ensures:
fastis not None → safe to accessfast.nextfast.nextis not None → safe to accessfast.next.next
What Happens Without Proper Checks
# 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 FalseExample crash:
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`
# WRONG
while fast:
fast = fast.next.next # Crashes if fast.next is NoneFix:
# CORRECT
while fast and fast.next:
fast = fast.next.nextPitfall 2: Accessing `.next.next` Without Checks
# WRONG
if slow.next.next: # Crashes if slow.next is None!
# ...Fix:
# CORRECT
if slow.next and slow.next.next:
# ...Pitfall 3: Forgetting Edge Cases
# 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:
# 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 slowPitfall 4: Wrong Initialization
# WRONG: fast starts at head.next without checking
def hasCycle(head):
slow = head
fast = head.next # Crashes if head is None!
# ...Fix:
# 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
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 slowWhy it's safe:
- Handles empty list (returns None)
- Checks
fast and fast.nextbefore advancing - Works for odd and even length lists
Test cases:
Empty: None → None
Single: 1 → 1
Two: 1->2 → 2
Three: 1->2->3 → 2
Four: 1->2->3->4 → 3Pattern 2: Detect Cycle
def hasCycle(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return FalseWhy it's safe:
- No explicit edge case check needed (loop handles it)
- If head is None,
fastis None, loop doesn't run - If single node,
fast.nextis None, loop doesn't run
Pattern 3: Remove Nth Node From End
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.nextWhy it's safe:
- Dummy node handles removing head
- Checks
fastbefore advancing - Ensures
slow.nextexists before removing
Pattern 4: Palindrome Linked List
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 TrueWhy it's safe:
- Edge case check at start
- Safe middle-finding
- Reversal checks
slowbefore 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
# Before: current.next
if current:
value = current.next # Safe✅ Double Pointer Access
# Before: current.next.next
if current and current.next:
value = current.next.next # Safe✅ Fast Pointer (2 steps)
# Before: fast.next.next
if fast and fast.next:
fast = fast.next.next # Safe✅ Slow Pointer (1 step)
# Before: slow.next
if slow:
slow = slow.next # SafeEdge Cases to Always Test
Edge Case 1: Empty List
head = None
# Your code should handle this without crashing
result = yourFunction(None)Edge Case 2: Single Node
head = ListNode(1)
head.next = None
# Fast pointer immediately becomes NoneEdge Case 3: Two Nodes
head = ListNode(1)
head.next = ListNode(2)
# Fast pointer reaches end after one iterationEdge Case 4: Cycle at Head
head = ListNode(1)
head.next = head # Points to itself
# Should detect cycle immediatelyDebugging Null Pointer Errors
When you get a null pointer error:
Step 1: Identify the Line
AttributeError: 'NoneType' object has no attribute 'next'
File "solution.py", line 12, in hasCycle
fast = fast.next.nextThe error is on line 12: fast.next.next
Step 2: Add Debug Prints
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
# 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
# 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 uses "None" for null
if node is None: # Preferred
if not node: # Also works
# Accessing None.next raises AttributeErrorJava
// Java uses "null"
if (node == null) {
// Handle null
}
// Accessing null.next throws NullPointerExceptionJavaScript
// 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 TypeErrorPractice Problems
To master safe linked list two pointers:
- Linked List Cycle (#141) - basic fast/slow with null checks
- Middle of Linked List (#876) - finding middle safely
- Remove Nth Node From End (#19) - careful pointer advancement
- Palindrome Linked List (#234) - multiple pointer operations
- 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:
if not head or not head.next:
return False
slow = head
fast = head.nextQ: 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:
- Always check
fast and fast.nextbeforefast.next.next - Check
nodebeforenode.next - Handle edge cases: empty, single node, two nodes
- Use
while fast and fast.next:as your standard loop condition - Test edge cases before submitting
The safe pattern:
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.nextRemember:
- 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
