72. Happy Number
๐ LeetCode Problem: 202. Happy Number
๐ Difficulty: Easy
๐ท๏ธ Topics: Hash Table, Math, Two Pointers
Problem Statement
Write an algorithm to determine if a number n is happy.
A happy number is a number defined by the following process:
- Starting with any positive integer, replace the number by the sum of the squares of its digits.
- Repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1.
- Those numbers for which this process ends in 1 are happy.
Return true if n is a happy number, and false if not.
Example 1:
Input: n = 19
Output: true
Explanation:
1ยฒ + 9ยฒ = 1 + 81 = 82
8ยฒ + 2ยฒ = 64 + 4 = 68
6ยฒ + 8ยฒ = 36 + 64 = 100
1ยฒ + 0ยฒ + 0ยฒ = 1 + 0 + 0 = 1
Example 2:
Input: n = 2
Output: false
Explanation:
2ยฒ = 4
4ยฒ = 16
1ยฒ + 6ยฒ = 1 + 36 = 37
3ยฒ + 7ยฒ = 9 + 49 = 58
5ยฒ + 8ยฒ = 25 + 64 = 89
8ยฒ + 9ยฒ = 64 + 81 = 145
1ยฒ + 4ยฒ + 5ยฒ = 1 + 16 + 25 = 42
4ยฒ + 2ยฒ = 16 + 4 = 20
2ยฒ + 0ยฒ = 4 + 0 = 4
(Back to 4, cycle detected!)
Constraints:
- 1 <= n <= 2^31 - 1
๐จ Visual Understanding
The Problem Visualized
Example 1: n = 19 (Happy Number!)
19 โ 1ยฒ + 9ยฒ = 82
82 โ 8ยฒ + 2ยฒ = 68
68 โ 6ยฒ + 8ยฒ = 100
100 โ 1ยฒ + 0ยฒ + 0ยฒ = 1 โ
Reaches 1 โ Happy!
Example 2: n = 2 (Not Happy!)
2 โ 2ยฒ = 4
4 โ 4ยฒ = 16
16 โ 1ยฒ + 6ยฒ = 37
37 โ 3ยฒ + 7ยฒ = 58
58 โ 5ยฒ + 8ยฒ = 89
89 โ 8ยฒ + 9ยฒ = 145
145 โ 1ยฒ + 4ยฒ + 5ยฒ = 42
42 โ 4ยฒ + 2ยฒ = 20
20 โ 2ยฒ + 0ยฒ = 4 โ Back to 4!
Cycle: 4 โ 16 โ 37 โ 58 โ 89 โ 145 โ 42 โ 20 โ 4
Never reaches 1 โ Not Happy!
Visual representation of cycle:
4 โโโโโโโโโโโโโโโโ
โ โ
16 20
โ โ
37 42
โ โ
58 145
โ โ
89 โโโโโโโโโโโโโโโโ
This is a CYCLE! Just like a linked list cycle!
๐จ CRITICAL INSIGHT - This is a Cycle Detection Problem!
The Key Pattern!
This problem is IDENTICAL to detecting a cycle in a linked list!
Instead of:
node.next โ next node
We have:
number โ sumOfSquares(number)
The "linked list":
19 โ 82 โ 68 โ 100 โ 1
2 โ 4 โ 16 โ 37 โ 58 โ 89 โ 145 โ 42 โ 20 โ 4 (cycle!)
Two possible outcomes:
1. Reaches 1 (happy) โ
2. Enters a cycle (not happy) โ
Use Floyd's Cycle Detection!
Slow: Computes next once
Fast: Computes next twice
If they meet at 1: Happy!
If they meet elsewhere: Cycle, not happy!
Why There's Always a Cycle or Reaches 1
Mathematical insight:
For any number, the sum of squares of digits is bounded!
Largest 10-digit number: 9,999,999,999
Sum of squares: 10 ร 9ยฒ = 810
So any number reduces to โค 3 digits quickly!
For 3-digit numbers (up to 999):
Max sum: 9ยฒ + 9ยฒ + 9ยฒ = 243
For 2-digit numbers (up to 99):
Max sum: 9ยฒ + 9ยฒ = 162
The sequence MUST eventually:
1. Reach 1 (happy) โ
2. Enter a cycle (not happy) โ
There are only so many numbers it can visit!
Eventually, it MUST repeat (cycle) or reach 1!
Common Cycle in Unhappy Numbers
The most common cycle for unhappy numbers:
4 โ 16 โ 37 โ 58 โ 89 โ 145 โ 42 โ 20 โ 4
Almost all unhappy numbers eventually enter this cycle!
Other possible cycles exist but are rare:
- Single digit unhappy: 2, 3, 4, 5, 6, 8, 9
- They all eventually lead to the 4-cycle!
๐ฏ Approach 1: Hash Set (Tracking Seen Numbers)
The Most Natural Thinking ๐ญ
Core Logic:
Keep track of numbers we've seen
If we see a number again โ cycle!
If we reach 1 โ happy!
Implementation
/**
* Using HashSet to detect cycle
* Time: O(log n), Space: O(log n)
*/
public boolean isHappy(int n) {
Set<Integer> seen = new HashSet<>();
while (n != 1 && !seen.contains(n)) {
seen.add(n);
n = getNext(n);
}
return n == 1;
}
// Calculate sum of squares of digits
private int getNext(int n) {
int sum = 0;
while (n > 0) {
int digit = n % 10;
sum += digit * digit;
n /= 10;
}
return sum;
}
โฐ Time: O(log n) - Number of digits reduces quickly
๐พ Space: O(log n) - Store seen numbers
โ Works well, but uses extra space!
๐ฏ Approach 2: Floyd's Cycle Detection (Optimal) โญ
Better Approach ๐ก
๐ง The Core Insight
Treat this as a linked list with cycles!
Instead of node.next, we have getNext(n)
Use fast & slow pointers:
Slow: computes next once
Fast: computes next twice
If they meet at 1: Happy!
If they meet at other number: Cycle, not happy!
The Strategy:
Floyd's Algorithm Applied:
1. Initialize slow = n, fast = n
2. Move slow one step: slow = getNext(slow)
3. Move fast two steps: fast = getNext(getNext(fast))
4. If they meet at 1 โ return true
5. If they meet elsewhere โ return false
Space: O(1) - No HashSet needed!
Implementation
/**
* Floyd's Cycle Detection for Happy Number
* Time: O(log n), Space: O(1)
*/
public boolean isHappy(int n) {
int slow = n;
int fast = n;
do {
slow = getNext(slow); // Move 1 step
fast = getNext(getNext(fast)); // Move 2 steps
} while (slow != fast);
// If they met at 1, it's happy!
return slow == 1;
}
// Calculate sum of squares of digits
private int getNext(int n) {
int sum = 0;
while (n > 0) {
int digit = n % 10;
sum += digit * digit;
n /= 10;
}
return sum;
}
โฐ Time: O(log n) - Sequence length is logarithmic
๐พ Space: O(1) - Only two variables!
๐ Dry Run
Example 1: n = 19 (Happy Number)
Goal: Check if reaches 1
Initial:
slow = 19
fast = 19
Iteration 1:
slow = getNext(19) = 1ยฒ + 9ยฒ = 82
fast = getNext(getNext(19))
= getNext(82)
= 8ยฒ + 2ยฒ = 68
= getNext(68)
= 6ยฒ + 8ยฒ = 100
slow = 82
fast = 100
slow != fast, continue
Iteration 2:
slow = getNext(82) = 8ยฒ + 2ยฒ = 68
fast = getNext(getNext(100))
= getNext(1ยฒ + 0ยฒ + 0ยฒ)
= getNext(1)
= 1ยฒ = 1
= getNext(1)
= 1ยฒ = 1
slow = 68
fast = 1
slow != fast, continue
Iteration 3:
slow = getNext(68) = 6ยฒ + 8ยฒ = 100
fast = getNext(getNext(1))
= getNext(1)
= 1
= getNext(1)
= 1
slow = 100
fast = 1
slow != fast, continue
Iteration 4:
slow = getNext(100) = 1ยฒ + 0ยฒ + 0ยฒ = 1
fast = getNext(getNext(1))
= getNext(1)
= 1
= getNext(1)
= 1
slow = 1
fast = 1
slow == fast! Loop ends
Return slow == 1? Yes! โ
True (Happy number!)
Example 2: n = 2 (Unhappy Number)
Initial:
slow = 2
fast = 2
Iteration 1:
slow = getNext(2) = 2ยฒ = 4
fast = getNext(getNext(2))
= getNext(4)
= 4ยฒ = 16
= getNext(16)
= 1ยฒ + 6ยฒ = 37
slow = 4
fast = 37
Continue...
(Several iterations later)
Eventually:
slow enters cycle: 4 โ 16 โ 37 โ 58 โ 89 โ 145 โ 42 โ 20 โ 4
fast also in cycle, moving twice as fast
They meet at some number in the cycle (say 58)
slow = 58
fast = 58
slow == fast! Loop ends
Return slow == 1? No!
False (Not happy!)
๐ฏ Why This Works - Deep Dive
The Transformation as a "Linked List":
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Think of each number as a "node":
number โ getNext(number) โ getNext(getNext(number)) โ ...
This creates an implicit "linked list"!
Example: n = 2
2 โ 4 โ 16 โ 37 โ 58 โ 89 โ 145 โ 42 โ 20 โ 4 (cycle!)
Just like: node1 โ node2 โ ... โ node8 โ node2 (cycle!)
The Two Outcomes:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Outcome 1: Reaches 1
19 โ 82 โ 68 โ 100 โ 1
Once at 1: getNext(1) = 1ยฒ = 1
Stays at 1 forever!
This is like a cycle of length 1 at node "1"
Both slow and fast eventually reach 1
They meet at 1 โ
Outcome 2: Enters a cycle (not containing 1)
2 โ 4 โ 16 โ 37 โ 58 โ 89 โ 145 โ 42 โ 20 โ 4
Forms a cycle that doesn't include 1
Both slow and fast enter this cycle
Fast catches slow inside the cycle
They meet at some number != 1 โ
Floyd's Algorithm Detects Both:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
If happy:
Both reach 1
Meet at 1
Return true โ
If not happy:
Both enter cycle
Meet at some number != 1
Return false โ
Why Do-While Loop?
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
do {
slow = getNext(slow);
fast = getNext(getNext(fast));
} while (slow != fast);
Why not while loop?
At start: slow = n, fast = n
They're equal!
While loop would exit immediately!
Do-while ensures:
Move first, then check
Always executes at least once โ
Time Complexity:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Key insight: Sequence length is bounded!
For a d-digit number:
Next value โค d ร 81 (since 9ยฒ = 81)
Example: 999 (3 digits)
Next: 9ยฒ + 9ยฒ + 9ยฒ = 243 (3 digits still)
The sequence converges to small numbers quickly!
Cycle length is also bounded (small)
Common cycle: 4 โ 16 โ 37 โ 58 โ 89 โ 145 โ 42 โ 20 โ 4
Length: 8
Total iterations: O(log n)
Each iteration: O(log n) to compute getNext
Overall: O(log n) โ
Space: O(1) - Only two variables!
Why getNext Computation is O(log n)
Computing sum of squares of digits:
int getNext(int n) {
int sum = 0;
while (n > 0) {
int digit = n % 10;
sum += digit * digit;
n /= 10;
}
return sum;
}
Number of iterations = number of digits = logโโ(n)
Example: n = 12345
5 digits โ 5 iterations
logโโ(12345) โ 4.09 โ 5 โ
So getNext is O(log n)
โ ๏ธ Common Mistakes to Avoid
Mistake 1: Using while instead of do-while
// โ WRONG - Exits immediately if slow == fast at start
while (slow != fast) {
slow = getNext(slow);
fast = getNext(getNext(fast));
}
// At start, slow = n, fast = n, loop never executes!
// โ CORRECT - Always executes at least once
do {
slow = getNext(slow);
fast = getNext(getNext(fast));
} while (slow != fast);
Mistake 2: Checking == 1 inside loop
// โ INEFFICIENT - Multiple checks
do {
slow = getNext(slow);
fast = getNext(getNext(fast));
if (slow == 1 || fast == 1) {
return true;
}
} while (slow != fast);
// โ CORRECT - Single check after meeting
do {
slow = getNext(slow);
fast = getNext(getNext(fast));
} while (slow != fast);
return slow == 1;
Mistake 3: Wrong getNext implementation
// โ WRONG - Doesn't handle multiple digits correctly
private int getNext(int n) {
return n * n; // Only squares the whole number!
}
// โ CORRECT - Squares each digit
private int getNext(int n) {
int sum = 0;
while (n > 0) {
int digit = n % 10;
sum += digit * digit;
n /= 10;
}
return sum;
}
Mistake 4: Not initializing both at n
// โ WRONG - Different starting points
int slow = n;
int fast = getNext(n); // Fast starts ahead
// โ CORRECT - Both start at n
int slow = n;
int fast = n;
Mistake 5: Calling getNext with 0
// โ POTENTIAL ISSUE - Though it works
// getNext(0) = 0, which creates immediate cycle
// โ BETTER - Handle explicitly if needed
if (n == 0) return false; // 0 is not happy
๐ฏ Pattern Recognition
Problem Type: Cycle Detection in Transformation Sequence
Core Pattern: Fast & Slow Pointers (Floyd's Algorithm)
When to Apply:
โ Sequence of transformations
โ Can eventually cycle or reach target
โ No explicit linked list structure
โ Need O(1) space
โ Function creates "next" relationship
Recognition Keywords:
- "Process ends in" (terminal state)
- "Loops endlessly" (cycle)
- "Replace by" (transformation)
- "Repeat the process"
- Number transformations
- Sequence of operations
Similar Problems:
- Linked List Cycle (LC 141) - Explicit linked list
- Find Duplicate Number (LC 287) - Array as linked list
- Circular Array Loop (LC 457) - Array with cycles
Key Components:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 1. Transformation function (getNext) โ
โ 2. Two outcomes: target or cycle โ
โ 3. Fast & slow pointers โ
โ 4. Meet at target or in cycle โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
This shows Floyd's algorithm works beyond linked lists!
๐ง Interview Strategy
Step 1: "This is cycle detection, not explicit linked list"
Step 2: "Use Floyd's algorithm with transformation function"
Step 3: "If meet at 1: happy; else: cycle, not happy"
Step 4: "O(log n) time, O(1) space"
Key Points to Mention:
- Each number โ next number via sum of squares
- Creates implicit "linked list"
- Two outcomes: reach 1 or enter cycle
- Use fast & slow pointers
- Do-while loop (both start at n)
- Meet at 1 โ happy, else โ not happy
- Time: O(log n), Space: O(1)
- Alternative: HashSet O(log n) space
Why This Approach?
"I recognize this as a cycle detection problem. Each number
transforms to the next via sum of squares of digits, creating
an implicit linked list. It either reaches 1 (happy) or enters
a cycle (not happy). I'll use Floyd's cycle detection with two
pointers moving at different speeds. When they meet, if they're
at 1, the number is happy. Otherwise, they met in a cycle."
Why Do-While?
"Since both pointers start at n, they're initially equal. A
while loop would exit immediately. The do-while ensures we
move both pointers at least once before checking if they meet."
Edge Cases to Mention:
โ n = 1 (already happy)
โ n = 2 (enters cycle immediately)
โ Small numbers (quick convergence)
โ Large numbers (rapid reduction)
๐ Quick Revision Notes
๐ฏ Core Concept:
Happy Number: Each number โ sum of squares of digits. Reaches 1 (happy โ) or enters cycle (not happy โ). Use Floyd's algorithm! Slow: next once, Fast: next twice. Do-while loop (both start at n). If meet at 1: happy. Else: cycle. O(log n) time, O(1) space!
โก Quick Implementation:
class Test {
public boolean isHappy(int n) {
// // Approach 1 - hashset
// // TC: O(log n) and SC: O(log n)
// HashSet<Integer> set = new HashSet<>();
// while (n != 1 && !set.contains(n)) {
// set.add(n); // add the incoming number
// int curr = n;
// int squaresSum = 0;
// // find the sum of squares of number.
// while (curr > 0) {
// int mod = curr % 10;
// squaresSum += Math.pow(mod, 2);
// curr = curr / 10;
// }
// n = squaresSum;
// }
// return n == 1;
// Approach 2 - using Floyd Algorithm
// TC: O(log n) and SC: O(1)
// Almost same as above. Instead of finding next num one at a time, we
// find 2 next numbers at a time. This gets equivalent to fast and slow
// pointers. When fast pointer lap on the slow pointer, cycle gets
// detected and the number is not happy.
int slow = n;
int fast = n;
while (fast != 1) { // loop till it becomes unhappy.
System.out.println("slow: "+slow+" and fast: "+fast);
slow = getSumOfSquares(slow);
fast = getSumOfSquares(getSumOfSquares(fast));
System.out.println("sslow: "+slow+" and fast: "+fast);
// break once cycle gets detected or
// when happy number found (edge case when both slow and fast are equal)
if(slow == fast && fast != 1) {
return false;
}
}
return true;
}
private int getSumOfSquares(int n) {
int curr = n;
int squaresSum = 0;
// find the sum of squares of number.
while (curr > 0) {
int mod = curr % 10;
squaresSum += Math.pow(mod, 2);
curr = curr / 10;
}
return squaresSum;
}
public static void main(String[] args) {
Test t = new Test();
System.out.println(t.isHappy(19) == true); // T
System.out.println(t.isHappy(2) == false); // F
System.out.println(t.isHappy(1) == true); // T
System.out.println(t.isHappy(2) == false); // F
System.out.println(t.isHappy(7) == true); // T
System.out.println(t.isHappy(116) == false); // F
System.out.println(t.isHappy(10) == true); // T
}
}
๐ Key Insights:
- Pattern: Fast & Slow Pointers on transformations
- Transform: n โ sum of squares of digits
- Implicit List: Each number โ next number
- Two Outcomes: Reach 1 (happy) or cycle (not happy)
- Do-While: Both start at n, need to move first
- Meet at 1: Happy! โ
- Meet elsewhere: Cycle, not happy โ
- getNext: O(log n) - number of digits
- Time: O(log n), Space: O(1)
๐ช Memory Aid:
"Sum squares of digits! Reach 1 or cycle! Floyd detects both!"
Think: "Implicit linked list with transformations!" ๐
๐ก The Key Insight:
This is a disguised linked list cycle problem!
Each number "points to" its next:
n โ getNext(n) โ getNext(getNext(n)) โ ...
Just like:
node โ node.next โ node.next.next โ ...
Floyd's algorithm works on ANY sequence!
โ ๏ธ Critical Details:
1. Transform: Sum of squares of each digit
2. getNext: Extract digits with % 10, divide by 10
3. Do-While: MUST use (not while)
4. Why? Both start equal, need to move first
5. Meet at 1: Return true (happy)
6. Meet elsewhere: Return false (cycle)
7. Common cycle: 4 โ 16 โ 37 โ 58 โ 89 โ 145 โ 42 โ 20 โ 4
8. Bounded: Sequences converge quickly
๐ฅ The getNext Function:
n = 19
Extract digits and square:
19 % 10 = 9 โ 9ยฒ = 81
19 / 10 = 1
1 % 10 = 1 โ 1ยฒ = 1
1 / 10 = 0 (done)
Sum: 81 + 1 = 82 โ
๐ก Why Do-While:
Initial state:
slow = 19
fast = 19
If using while (slow != fast):
19 != 19? No! Loop exits immediately โ
Using do-while:
Move first, THEN check
Always executes at least once โ
After first iteration:
slow = 82
fast = 100
Now they're different, loop continues โ
๐ฏ Common Unhappy Cycle:
Most unhappy numbers end up in this cycle:
4 โ 16 โ 37 โ 58 โ 89 โ 145 โ 42 โ 20 โ 4
Examples:
2 โ 4 โ (enters cycle)
3 โ 9 โ 81 โ 65 โ 61 โ 37 โ (in cycle)
5 โ 25 โ 29 โ 85 โ 89 โ (in cycle)
Almost all unhappy numbers eventually
reach this 8-number cycle!
๐งช Edge Cases
Case 1: Already happy
Input: n = 1
Output: true
(1ยฒ = 1, stays at 1)
Case 2: Simple unhappy
Input: n = 2
Output: false
(2 โ 4 โ 16 โ ... โ cycle)
Case 3: Large happy number
Input: n = 7
Output: true
(7 โ 49 โ 97 โ 130 โ 10 โ 1)
Case 4: Two digits happy
Input: n = 19
Output: true
(19 โ 82 โ 68 โ 100 โ 1)
Case 5: Three digits unhappy
Input: n = 116
Output: false
(Enters cycle)
All handled correctly! โ
Related Patterns
- Linked List Cycle (LC 141) - Explicit linked list
- Find Duplicate Number (LC 287) - Array as implicit list
- Circular Array Loop (LC 457) - Array with indices
Happy practicing! ๐ฏ
Note: This beautifully demonstrates Floyd's algorithm works on ANY sequence, not just linked lists! The transformation function creates an implicit "linked list" structure! ๐