202. Happy Number - Explanation
Description
A non-cyclical number is an integer defined by the following algorithm:
- Given a positive integer, replace it with the sum of the squares of its digits.
- Repeat the above step until the number equals
1, or it loops infinitely in a cycle which does not include1. - If it stops at
1, then the number is a non-cyclical number.
Given a positive integer n, return true if it is a non-cyclical number, otherwise return false.
Example 1:
Input: n = 100
Output: trueExplanation: 1² + 0² + 0² = 1
Example 2:
Input: n = 101
Output: falseExplanation:1² + 0² + 1² = 22² = 44² = 161² + 6² = 373² + 7² = 585² + 8² = 898² + 9² = 1451² + 4² + 5² = 424² + 2² = 202² + 0² = 4 (This number has already been seen)
Constraints:
1 <= n <= 1000
Topics
Recommended Time & Space Complexity
You should aim for a solution as good or better than O(logn) time and O(logn) space, where n is the given integer.
Hint 1
Create a helper function that returns the sum of the squares of a number's digits. Then, simulate the given process. If we reach 1, return true. However, we may get stuck in a cycle if a number is processed more than once. What data structure can be used to detect if a number has already been processed?
Hint 2
We can use a hash set to detect if a number has already been processed. At each step, we update n with the return value of the helper function. If the result is 1, we return true. If n is already in the set, we return false. Otherwise, we add n to the hash set and continue.
Prerequisites
Before attempting this problem, you should be comfortable with:
- Hash Set - Detecting previously seen values to identify cycles
- Floyd's Cycle Detection (Fast/Slow Pointers) - Detecting cycles without extra space
- Digit Manipulation - Extracting digits using modulo and integer division
1. Hash Set
Intuition
A number is called happy if repeatedly replacing it with the sum of the squares of its digits eventually leads to 1.
While doing this process, only two things can happen:
- we eventually reach
1→ the number is happy - we fall into a cycle and repeat numbers forever → the number is not happy
So the key problem is cycle detection.
A simple and beginner-friendly way to detect a cycle is to:
- keep a set of numbers we have already seen
- if a number repeats, we are stuck in a loop and will never reach
1
Algorithm
- Initialize an empty set
visitto store numbers we have already seen. - While
nis not invisit:- add
ntovisit - replace
nwith the sum of the squares of its digits - if
nbecomes1, returntrue
- add
- If we exit the loop, it means
nrepeated:- a cycle is detected
- return
false
Helper Function (Sum of Squares)
To compute the next number:
- Initialize
output = 0 - While
n > 0:- extract the last digit using
n % 10 - square it and add to
output - remove the digit using
n //= 10
- extract the last digit using
- Return
output
class Solution:
def isHappy(self, n: int) -> bool:
visit = set()
while n not in visit:
visit.add(n)
n = self.sumOfSquares(n)
if n == 1:
return True
return False
def sumOfSquares(self, n: int) -> int:
output = 0
while n:
digit = n % 10
digit = digit ** 2
output += digit
n = n // 10
return outputTime & Space Complexity
- Time complexity:
- Space complexity:
2. Fast And Slow Pointers - I
Intuition
A number is happy if repeatedly replacing it with the sum of the squares of its digits eventually reaches 1.
Just like the hash set approach, the process can:
- reach
1→ happy number - fall into a cycle → not a happy number
Instead of storing all visited numbers, we can detect a cycle using the fast and slow pointers technique (also known as Floyd's cycle detection).
The idea:
- treat the transformation
n → sumOfSquares(n)like moving through a linked list - use two pointers:
slowmoves one step at a timefastmoves two steps at a time
- if there is a cycle,
slowandfastwill eventually meet - if the cycle includes
1, then the number is happy
This avoids extra memory and still reliably detects cycles.
Algorithm
- Initialize:
slow = nfast = sumOfSquares(n)
- While
slow != fast:- move
slowone step:slow = sumOfSquares(slow)
- move
fasttwo steps:fast = sumOfSquares(sumOfSquares(fast))
- move
- When the loop ends, a cycle is detected.
- If
fast == 1:- the cycle ends at
1 - return
true
- the cycle ends at
- Otherwise:
- the cycle does not include
1 - return
false
- the cycle does not include
Helper Function (Sum of Squares)
To compute the next number:
- Initialize
output = 0 - While
n > 0:- extract the last digit using
n % 10 - square it and add to
output - remove the digit using
n //= 10
- extract the last digit using
- Return
output
class Solution:
def isHappy(self, n: int) -> bool:
slow, fast = n, self.sumOfSquares(n)
while slow != fast:
fast = self.sumOfSquares(fast)
fast = self.sumOfSquares(fast)
slow = self.sumOfSquares(slow)
return True if fast == 1 else False
def sumOfSquares(self, n: int) -> int:
output = 0
while n:
digit = n % 10
digit = digit ** 2
output += digit
n = n // 10
return outputTime & Space Complexity
- Time complexity:
- Space complexity:
3. Fast And Slow Pointers - II
Intuition
A number is happy if repeatedly replacing it with the sum of the squares of its digits eventually reaches 1.
Just like before, this process either:
- reaches
1→ happy number - falls into a cycle → not a happy number
This solution uses a different cycle detection method called Brent's Algorithm, which is another form of fast–slow pointer technique.
Key idea:
- We still move through the sequence
n → sumOfSquares(n) - But instead of moving one pointer twice as fast every step, we:
- increase the distance between comparisons in powers of two
- This reduces the number of comparisons and still guarantees cycle detection
We keep track of:
slow→ a checkpoint valuefast→ the moving valuepower→ how far we go before resettingslowlam→ current distance since last reset
If fast ever equals slow, a cycle is detected.
Algorithm
- Initialize:
slow = nfast = sumOfSquares(n)power = 1(current block size)lam = 1(steps taken in current block)
- While
slow != fast: - If
power == lam:- move the checkpoint:
slow = fast
- double the block size:
power *= 2
- reset step counter:
lam = 0
- move the checkpoint:
- Move
fastone step forward:fast = sumOfSquares(fast)
- Increment
lamby1. - When the loop ends, a cycle is detected.
- If
fast == 1:- the cycle ends at
1 - return
true
- the cycle ends at
- Otherwise:
- return
false
- return
Helper Function (Sum of Squares)
To compute the next number:
- Initialize
output = 0 - While
n > 0:- extract the last digit using
n % 10 - square it and add to
output - remove the digit using
n //= 10
- extract the last digit using
- Return
output
class Solution:
def isHappy(self, n: int) -> bool:
slow, fast = n, self.sumOfSquares(n)
power = lam = 1
while slow != fast:
if power == lam:
slow = fast
power *= 2
lam = 0
fast = self.sumOfSquares(fast)
lam += 1
return True if fast == 1 else False
def sumOfSquares(self, n: int) -> int:
output = 0
while n:
digit = n % 10
digit = digit ** 2
output += digit
n = n // 10
return outputTime & Space Complexity
- Time complexity:
- Space complexity:
Common Pitfalls
Infinite Loop Without Cycle Detection
Forgetting to detect cycles causes the program to loop forever for non-happy numbers. Without a hash set or fast/slow pointers, numbers like 2 will endlessly cycle through the same values (2 → 4 → 16 → 37 → 58 → 89 → 145 → 42 → 20 → 4 → ...) without ever reaching 1.
Incorrect Digit Extraction
Using the wrong operations to extract digits leads to incorrect sum calculations. A common mistake is forgetting integer division when removing the last digit (n = n // 10) or using string conversion inefficiently. Always use n % 10 to get the last digit and n // 10 to remove it.
Sign in to join the discussion