Loading problems...
TL;DR: We detect a cycle in the sequence of numbers generated by summing the squares of digits; if the cycle stabilizes at 1, the number is happy, otherwise, it is not.
The Happy Number problem asks us to determine the fate of a specific numerical process starting with a given integer n. In this process, we replace the current number with the sum of the squares of its digits. We repeat this indefinitely. There are two possible outcomes: either the process eventually reaches the number 1 (where it stays, as ), or it enters a cycle that does not include 1.
Our goal in the LeetCode 202 solution is to return true if reaches 1, and if it gets stuck in a loop. This is a classic interview question that tests the ability to recognize implicit data structures and cycle detection mechanisms.
nfalseThe most intuitive approach is to simulate the process and store every number we encounter in a data structure, such as a Hash Set.
seen.n.n is 1, return true.n is already in seen, we have detected a cycle that is not 1. Return false.n to seen and repeat.Pseudo-code:
textfunction isHappy(n): seen = new HashSet() while n != 1: if seen.contains(n): return false seen.add(n) n = sumOfSquares(n) return true
Why this approach is suboptimal: While this solution is correct, its Space Complexity is , where is the number of distinct integers in the sequence before a cycle is found. In a constrained environment or systems with limited memory, allocating a hash table to store history is unnecessary overhead when we only need to detect the existence of a cycle, not the full history.
The key insight for the optimal LeetCode 202 solution is to recognize that the sequence of numbers forms an implicit LinkedList structure.
Given a number , the next number is strictly deterministic: . Because the transformation is deterministic, if we ever revisit a number, we are in a cycle.
Instead of storing the history, we can use two pointers moving at different speeds:
The Invariant: If a cycle exists (which happens if the number is not happy), the Fast Pointer will eventually "lap" or catch up to the Slow Pointer within the cycle. If the number is happy, the Fast Pointer will reach the value 1.
Visual Description: Imagine a graph where every node has exactly one outgoing edge pointing to the sum of the squares of its digits.
n is happy, the path looks like a straight line ending at a node with value 1, which loops to itself ().n is not happy, the path leads into a closed loop of numbers (e.g., ).
Let's trace n = 19 (Happy Case):
slow = 19, fast = 19.slow moves to .fast moves to , then .slow = 82, fast = 68. No collision.slow moves to .fast moves to , then .fast is 1. The loop terminates. Return true.Let's trace n = 2 (Unhappy Case):
slow = 2, fast = 2.slow becomes 4.fast becomes 4, then 16.slow becomes 16.fast becomes , then .slow enters the cycle and fast is already circulating inside it.fast moves faster, it will eventually land on the same number as slow.slow == fast (and not 1). Return false.Termination: One might ask: "Will the numbers grow infinitely?" No. Consider the maximum next value. For a 3-digit number (max 999), the max sum of squares is . For a 4-digit number (max 9999), the max sum is . For any number , the sum of the squares of its digits is strictly less than . Therefore, the sequence must eventually fall below 1000 (specifically below 163). Once in this bounded range, by the Pigeonhole Principle, the sequence must either reach 1 or repeat a number (form a cycle).
Cycle Detection: Floyd’s Cycle-Finding Algorithm guarantees that if a cycle exists, the fast and slow pointers will meet. Since the sequence is deterministic and bounded, a collision is inevitable if 1 is not reached.
cppclass Solution { public: // Helper function to calculate sum of squares of digits int getNext(int n) { int totalSum = 0; while (n > 0) { int d = n % 10; n = n / 10; totalSum += d * d; } return totalSum; } bool isHappy(int n) { int slow = n; int fast = getNext(n); // Advance fast one step ahead initially to start loop // Continue until fast reaches 1 (happy) or fast meets slow (cycle) while (fast != 1 && slow != fast) { slow = getNext(slow); // Move slow by 1 step fast = getNext(getNext(fast)); // Move fast by 2 steps } return fast == 1; } };
javaclass Solution { // Helper function to calculate sum of squares of digits private int getNext(int n) { int totalSum = 0; while (n > 0) { int d = n % 10; n = n / 10; totalSum += d * d; } return totalSum; } public boolean isHappy(int n) { int slow = n; int fast = getNext(n); // Standard Floyd's Cycle Detection logic while (fast != 1 && slow != fast) { slow = getNext(slow); fast = getNext(getNext(fast)); } return fast == 1; } }
pythonclass Solution: def isHappy(self, n: int) -> bool: def get_next(number): total_sum = 0 while number > 0: number, digit = divmod(number, 10) total_sum += digit ** 2 return total_sum slow = n fast = get_next(n) # Loop until we find 1 or detect a cycle while fast != 1 and slow != fast: slow = get_next(slow) fast = get_next(get_next(fast)) return fast == 1
slow and fast) regardless of the input size. We do not maintain a hash set of history.The Two Pointers - Fast & Slow pattern is a powerful tool for detecting cycles in linear data structures. It applies directly to:
Mastering LeetCode 202 solidifies your understanding of implicit graphs and cycle detection without auxiliary space.
Why does the naive solution get Time Limit Exceeded (TLE)?
n is not happy, the sequence enters an infinite loop. Without cycle detection (Set or Fast/Slow pointers), the while loop never terminates.Why does the recursion depth error occur?
Why initialize fast = getNext(n) instead of fast = n?
slow = 100, fast = 1.n and using a while slow != fast loop immediately.n, the condition slow != fast is false immediately, and the loop never executes.false or fail to run logic. You must either use a do-while structure or offset the pointers initially.