Facebook Pixel

202. Happy Number

Problem Description

A happy number is determined through a specific iterative process. Given a positive integer n, you need to determine whether it's a happy number or not.

The process works as follows:

  1. Take any positive integer as the starting point
  2. Calculate the sum of the squares of its digits
  3. Replace the original number with this sum
  4. Repeat steps 2-3 continuously

This process leads to one of two outcomes:

  • The number eventually reaches 1 and stays there (making it a happy number)
  • The number enters an endless cycle that never reaches 1 (making it not a happy number)

For example, if we start with n = 19:

  • 1² + 9² = 1 + 81 = 82
  • 8² + 2² = 64 + 4 = 68
  • 6² + 8² = 36 + 64 = 100
  • 1² + 0² + 0² = 1 + 0 + 0 = 1

Since we reached 1, the number 19 is happy.

Your task is to implement a function that takes a positive integer n and returns true if it's a happy number, or false otherwise.

The key challenge is detecting when the process enters a cycle (repeats a number we've seen before), which indicates the number will never reach 1 and is therefore not happy.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is recognizing that this process will either converge to 1 or get stuck in a cycle. Since we're repeatedly applying the same transformation (sum of squares of digits), and there are only a finite number of possible results for any given number of digits, we must eventually either reach 1 or revisit a number we've seen before.

Think about it this way: if we have a 3-digit number, the maximum sum of squares we can get is 9² + 9² + 9² = 243. For any number, the sum of squares of its digits is bounded, which means we're working within a finite space of possible values.

This naturally leads us to the idea of cycle detection. If we keep track of all the numbers we've encountered during the process, we can detect when we've entered a cycle by checking if we've seen the current number before.

The algorithm becomes straightforward:

  1. Keep a set to store all numbers we've visited
  2. For each number, calculate the sum of squares of its digits
  3. If we reach 1, we've found a happy number
  4. If we encounter a number we've already seen, we've detected a cycle and the number is not happy
  5. Otherwise, add the current number to our visited set and continue

The use of a set for tracking visited numbers gives us O(1) lookup time to check if we've entered a cycle, making this approach efficient. The process must terminate because we either reach 1 or detect a cycle within a bounded number of steps.

Solution Approach

The implementation uses a set-based cycle detection approach to determine if a number is happy.

Data Structure Used:

  • A set called vis to track all numbers we've encountered during the process

Algorithm Steps:

  1. Initialize tracking: Create an empty set vis to store visited numbers.

  2. Main loop condition: Continue the process while two conditions are met:

    • The current number n is not equal to 1
    • The current number n has not been seen before (not in vis)
  3. Track current number: Add the current n to the visited set before processing it.

  4. Calculate sum of squares of digits:

    • Initialize a variable x = 0 to accumulate the sum
    • Extract each digit using divmod(n, 10):
      • divmod(n, 10) returns (quotient, remainder) where remainder is the last digit
      • Square the digit (v * v) and add to x
      • Update n to the quotient to process remaining digits
    • After processing all digits, update n = x for the next iteration
  5. Determine result: When the loop exits, check if n == 1:

    • If n == 1, the number is happy (return True)
    • If loop exited because n was in vis, we found a cycle (return False)

Example walkthrough with n = 19:

  • Iteration 1: n = 19, extract digits: 1² + 9² = 82
  • Iteration 2: n = 82, extract digits: 8² + 2² = 68
  • Iteration 3: n = 68, extract digits: 6² + 8² = 100
  • Iteration 4: n = 100, extract digits: 1² + 0² + 0² = 1
  • Loop exits with n = 1, return True

The time complexity is O(log n) for extracting digits, and space complexity is O(k) where k is the number of unique numbers encountered before reaching 1 or detecting a cycle.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's trace through the algorithm with n = 2 to see how cycle detection works:

Initial Setup:

  • vis = {} (empty set)
  • n = 2

Iteration 1:

  • Check: n != 1 ✓ and n not in vis ✓ → Continue
  • Add 2 to vis: vis = {2}
  • Calculate sum of squares: 2² = 4
  • Update: n = 4

Iteration 2:

  • Check: n != 1 ✓ and n not in vis ✓ → Continue
  • Add 4 to vis: vis = {2, 4}
  • Calculate sum of squares: 4² = 16
  • Update: n = 16

Iteration 3:

  • Check: n != 1 ✓ and n not in vis ✓ → Continue
  • Add 16 to vis: vis = {2, 4, 16}
  • Calculate sum of squares: 1² + 6² = 1 + 36 = 37
  • Update: n = 37

Iteration 4:

  • Check: n != 1 ✓ and n not in vis ✓ → Continue
  • Add 37 to vis: vis = {2, 4, 16, 37}
  • Calculate sum of squares: 3² + 7² = 9 + 49 = 58
  • Update: n = 58

Iteration 5:

  • Check: n != 1 ✓ and n not in vis ✓ → Continue
  • Add 58 to vis: vis = {2, 4, 16, 37, 58}
  • Calculate sum of squares: 5² + 8² = 25 + 64 = 89
  • Update: n = 89

Iteration 6:

  • Check: n != 1 ✓ and n not in vis ✓ → Continue
  • Add 89 to vis: vis = {2, 4, 16, 37, 58, 89}
  • Calculate sum of squares: 8² + 9² = 64 + 81 = 145
  • Update: n = 145

Iteration 7:

  • Check: n != 1 ✓ and n not in vis ✓ → Continue
  • Add 145 to vis: vis = {2, 4, 16, 37, 58, 89, 145}
  • Calculate sum of squares: 1² + 4² + 5² = 1 + 16 + 25 = 42
  • Update: n = 42

Iteration 8:

  • Check: n != 1 ✓ and n not in vis ✓ → Continue
  • Add 42 to vis: vis = {2, 4, 16, 37, 58, 89, 145, 42}
  • Calculate sum of squares: 4² + 2² = 16 + 4 = 20
  • Update: n = 20

Iteration 9:

  • Check: n != 1 ✓ and n not in vis ✓ → Continue
  • Add 20 to vis: vis = {2, 4, 16, 37, 58, 89, 145, 42, 20}
  • Calculate sum of squares: 2² + 0² = 4 + 0 = 4
  • Update: n = 4

Iteration 10:

  • Check: n != 1 ✓ but n in vis ✗ (4 is already in our set!)
  • Loop exits because we detected a cycle

Result: Since n = 4 ≠ 1, return False. The number 2 is not happy.

The cycle we detected is: 4 → 16 → 37 → 58 → 89 → 145 → 42 → 20 → 4 (repeats)

Solution Implementation

1class Solution:
2    def isHappy(self, n: int) -> bool:
3        # Set to track visited numbers to detect cycles
4        visited = set()
5      
6        # Continue until we reach 1 (happy) or find a cycle (not happy)
7        while n != 1 and n not in visited:
8            # Add current number to visited set
9            visited.add(n)
10          
11            # Calculate sum of squares of digits
12            sum_of_squares = 0
13            while n > 0:
14                # Extract last digit using divmod
15                n, digit = divmod(n, 10)
16                # Add square of digit to sum
17                sum_of_squares += digit * digit
18          
19            # Update n with the new sum for next iteration
20            n = sum_of_squares
21      
22        # Return True if we reached 1, False if we found a cycle
23        return n == 1
24
1class Solution {
2    /**
3     * Determines if a number is a "happy number".
4     * A happy number is defined by the following process:
5     * - Starting with any positive integer, replace the number by the sum of the squares of its digits
6     * - Repeat the process until the number equals 1 (happy) or loops endlessly in a cycle (not happy)
7     * 
8     * @param n The positive integer to check
9     * @return true if n is a happy number, false otherwise
10     */
11    public boolean isHappy(int n) {
12        // Set to track visited numbers and detect cycles
13        Set<Integer> visitedNumbers = new HashSet<>();
14      
15        // Continue until we reach 1 (happy) or find a cycle (not happy)
16        while (n != 1 && !visitedNumbers.contains(n)) {
17            // Mark current number as visited
18            visitedNumbers.add(n);
19          
20            // Calculate sum of squares of digits
21            int sumOfSquares = 0;
22            while (n != 0) {
23                int digit = n % 10;  // Extract last digit
24                sumOfSquares += digit * digit;  // Add square of digit to sum
25                n /= 10;  // Remove last digit
26            }
27          
28            // Update n with the new sum for next iteration
29            n = sumOfSquares;
30        }
31      
32        // If we exited because n equals 1, it's happy; otherwise, we found a cycle
33        return n == 1;
34    }
35}
36
1class Solution {
2public:
3    bool isHappy(int n) {
4        // Use a hash set to track numbers we've seen to detect cycles
5        unordered_set<int> visitedNumbers;
6      
7        // Continue until we either reach 1 (happy number) or find a cycle
8        while (n != 1 && !visitedNumbers.count(n)) {
9            // Mark current number as visited
10            visitedNumbers.insert(n);
11          
12            // Calculate the sum of squares of digits
13            int sumOfSquares = 0;
14            while (n > 0) {
15                int digit = n % 10;  // Extract the last digit
16                sumOfSquares += digit * digit;  // Add square of digit to sum
17                n /= 10;  // Remove the last digit
18            }
19          
20            // Update n with the new sum for next iteration
21            n = sumOfSquares;
22        }
23      
24        // If we exited because n == 1, it's a happy number
25        return n == 1;
26    }
27};
28
1/**
2 * Determines if a number is a "happy number"
3 * A happy number is defined by the following process:
4 * - Starting with any positive integer, replace the number by the sum of the squares of its digits
5 * - Repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle
6 * - Numbers for which this process ends in 1 are happy numbers
7 * 
8 * @param n - The number to check
9 * @returns true if n is a happy number, false otherwise
10 */
11function isHappy(n: number): boolean {
12    /**
13     * Calculates the sum of squares of digits for a given number
14     * For example: 19 -> 1^2 + 9^2 = 82
15     * 
16     * @param num - The input number
17     * @returns Sum of squares of all digits
18     */
19    const getNext = (num: number): number => {
20        let sumOfSquares: number = 0;
21      
22        // Extract each digit and add its square to the sum
23        while (num !== 0) {
24            const digit: number = num % 10;
25            sumOfSquares += digit ** 2;
26            num = Math.floor(num / 10);
27        }
28      
29        return sumOfSquares;
30    };
31  
32    // Set to track visited numbers and detect cycles
33    const visitedNumbers: Set<number> = new Set<number>();
34  
35    // Continue the process until we reach 1 (happy) or find a cycle (not happy)
36    while (n !== 1) {
37        const nextNumber: number = getNext(n);
38      
39        // If we've seen this number before, we're in a cycle
40        if (visitedNumbers.has(nextNumber)) {
41            return false;
42        }
43      
44        // Track this number as visited
45        visitedNumbers.add(nextNumber);
46      
47        // Move to the next number in the sequence
48        n = nextNumber;
49    }
50  
51    // We reached 1, so it's a happy number
52    return true;
53}
54

Time and Space Complexity

Time Complexity: O(log n)

The algorithm repeatedly calculates the sum of squares of digits until either reaching 1 or detecting a cycle. For each number processed:

  • Extracting digits takes O(log n) time (where n is the current number, as it has log₁₀(n) digits)
  • The sequence of numbers will either reach 1 or enter a cycle within O(log n) iterations

The key insight is that the sum of squares of digits reduces the number significantly. For a k-digit number, the maximum sum of squares is 9² × k = 81k. Since k ≈ log₁₀(n), the numbers quickly become bounded. Once bounded, the sequence must either reach 1 or cycle within a constant number of steps.

Therefore, the overall time complexity is O(log n) iterations × O(log n) per iteration = O(log² n), but since the numbers become small quickly after the first few iterations, the amortized complexity is O(log n).

Space Complexity: O(log n)

The space is used by the vis set to store previously seen numbers. In the worst case before detecting a cycle or reaching 1, we store O(log n) numbers in the set, as the sequence length before cycling is bounded by O(log n).

Common Pitfalls

1. Modifying n while calculating the sum of squares

The Pitfall: A common mistake is directly modifying n while simultaneously trying to use it for the sum calculation, leading to incorrect results or infinite loops:

# INCORRECT - modifying n while still needing it
def isHappy(self, n: int) -> bool:
    visited = set()
    while n != 1 and n not in visited:
        visited.add(n)
        sum_of_squares = 0
        while n > 0:
            digit = n % 10
            sum_of_squares += digit * digit
            n = n // 10  # Problem: n becomes 0 after this loop
        # n is now 0, not sum_of_squares!
    return n == 1  # Always returns False

The Solution: Use a separate variable to accumulate the sum, then update n after the calculation is complete:

# CORRECT - preserve n until calculation is done
sum_of_squares = 0
while n > 0:
    n, digit = divmod(n, 10)
    sum_of_squares += digit * digit
n = sum_of_squares  # Update n after calculation

2. Checking visited set after adding the current number

The Pitfall: Adding n to the visited set before checking can cause immediate false cycle detection:

# INCORRECT - adds n before checking
def isHappy(self, n: int) -> bool:
    visited = set()
    while n != 1:
        visited.add(n)
        if n in visited:  # This will always be True!
            return False
        # ... rest of code

The Solution: Check if n is in the visited set BEFORE adding it:

# CORRECT - check before adding
while n != 1 and n not in visited:
    visited.add(n)
    # ... rest of code

3. Not handling the initial value of 1

The Pitfall: If the input n is already 1, the code might incorrectly add it to visited and process it:

# POTENTIAL ISSUE - unnecessary processing when n=1
def isHappy(self, n: int) -> bool:
    visited = set()
    while n not in visited:  # Missing n != 1 check
        visited.add(n)
        # ... calculate sum of squares
    return n == 1

The Solution: Include n != 1 in the loop condition to immediately return True for n=1:

# CORRECT - handles n=1 efficiently
while n != 1 and n not in visited:
    # Process only if n is not 1 and not visited

4. Integer overflow concerns (language-specific)

The Pitfall: In languages with fixed integer sizes, squaring large digits repeatedly might cause overflow:

# In Python this isn't an issue, but in C++ or Java:
// sum_of_squares += digit * digit; // Could overflow for very large numbers

The Solution: For Python, this isn't a concern due to arbitrary precision integers. For other languages, consider using appropriate data types or adding bounds checking. The maximum sum of squares for any number is bounded (e.g., for a 32-bit integer, the maximum is 9²×10 = 810), so overflow is rarely an actual issue in this problem.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What is the best way of checking if an element exists in an unsorted array once in terms of time complexity? Select the best that applies.


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More