Introduction

A happy number is a genuinely fascinating problem—not because the core digit manipulation is hard (it's just squaring digits and summing them, something you've done many times in this series), but because of a subtle trap: a naive implementation can loop forever if you don't explicitly detect when you've entered a repeating cycle.

This makes happy numbers one of the best introductions to a whole category of important algorithmic thinking: cycle detection.

In this guide, you'll learn:

Advertisement
  • What makes a number "happy"
  • Why the naive approach risks an infinite loop
  • How to fix it using a HashSet to track previously seen values
  • An even more elegant (and memory-efficient) approach called Floyd's Cycle Detection Algorithm
  • The surprising mathematical fact that every non-happy number eventually falls into the exact same repeating cycle

What Is a Happy Number?

A happy number is defined by a specific process:

  1. Take a number.
  2. Replace it with the sum of the squares of its digits.
  3. Repeat this process.
  4. If the sequence eventually reaches 1 (and stays there), the original number is happy.
  5. If it never reaches 1, but instead falls into an endless repeating loop of other numbers, the original number is unhappy.

For example, 19 is a happy number:

 
19
↓
1² + 9² = 82
↓
8² + 2² = 68
↓
6² + 8² = 100
↓
1² + 0² + 0² = 1
 

Since the sequence reaches 1, 19 is a happy number.


The Core Problem: Why This Can Loop Forever

Here's the trap: if you tried to check an unhappy number using a naive while (num != 1) loop, the program would run forever, since unhappy numbers never actually reach 1—they cycle endlessly through a repeating sequence of other values instead.

This is exactly why happy number checking absolutely requires some form of cycle detection. Without it, your program has no way of knowing it's stuck in a loop rather than still making progress toward 1.

For example, consider this naive implementation:

 
while (num != 1) {
    num = sumOfSquaredDigits(num);
}
 

This works perfectly for happy numbers, but if the number is unhappy, the loop never terminates because it continues cycling forever.


Method 1: Using a HashSet to Detect Cycles

The most common and straightforward solution is to track every intermediate value using a HashSet.

If the same value ever appears twice, you've detected a cycle, confirming that the number is unhappy.

 
import java.util.HashSet;

public class HappyNumberHashSet {

    static int sumOfSquaredDigits(int num) {
        int sum = 0;

        while (num != 0) {
            int digit = num % 10;
            sum += digit * digit;
            num /= 10;
        }

        return sum;
    }

    static boolean isHappy(int num) {
        HashSet<Integer> seen = new HashSet<>();

        while (num != 1 && !seen.contains(num)) {
            seen.add(num);
            num = sumOfSquaredDigits(num);
        }

        return num == 1;
    }

    public static void main(String[] args) {
        int num = 19;

        System.out.println(
                num + (isHappy(num)
                        ? " is a happy number."
                        : " is not a happy number."));
    }
}
 

How This Works

The seen HashSet records every intermediate value produced by the sum-of-squared-digits process.

The loop continues only while:

  • num has not reached 1, and
  • num has not already appeared in the HashSet.

The moment either condition becomes false, the loop stops.

There are two possible outcomes:

  • If the sequence reaches 1, the number is happy.
  • If a previously seen value appears again, the sequence has entered a cycle, proving the number is unhappy.

Output

 
19 is a happy number.
 

Example with an Unhappy Number

Let's test the algorithm using 4.

 
4
↓
16
↓
37
↓
58
↓
89
↓
145
↓
42
↓
20
↓
4
 

Notice that we've returned to 4, which we've already seen before.

This confirms that the sequence has entered a repeating cycle.

Since the sequence never reaches 1, 4 is not a happy number.


Method 2: Using Floyd's Cycle Detection (Tortoise and Hare)

A more memory-efficient alternative avoids the HashSet entirely by using Floyd's Cycle Detection Algorithm, also known as the Tortoise and Hare Algorithm.

This famous algorithm is commonly used to detect cycles in linked lists, but it works just as well here because the happy number sequence also behaves like repeatedly following links from one value to the next.

 
public class HappyNumberFloyd {

    static int sumOfSquaredDigits(int num) {
        int sum = 0;

        while (num != 0) {
            int digit = num % 10;
            sum += digit * digit;
            num /= 10;
        }

        return sum;
    }

    static boolean isHappy(int num) {

        int slow = num;
        int fast = sumOfSquaredDigits(num);

        while (fast != 1 && slow != fast) {
            slow = sumOfSquaredDigits(slow);
            fast = sumOfSquaredDigits(sumOfSquaredDigits(fast));
        }

        return fast == 1;
    }

    public static void main(String[] args) {
        int num = 19;

        System.out.println(
                num + (isHappy(num)
                        ? " is a happy number."
                        : " is not a happy number."));
    }
}
 

How This Works

Instead of storing every intermediate value, Floyd's algorithm uses two variables:

  • slow
  • fast

Both repeatedly apply the sum-of-squared-digits transformation.

However, they move at different speeds:

  • slow applies the transformation once during each iteration.
  • fast applies the transformation twice during each iteration.

If the sequence is happy, the fast pointer eventually reaches 1 first.

If the sequence is unhappy, the values enter a cycle. Since the fast pointer moves twice as quickly, it eventually catches up with the slow pointer, just like a faster runner eventually catches a slower runner on a circular track.

When slow == fast, you've detected a cycle without storing any previous values.

Why This Uses Less Memory

Unlike Method 1, which stores every intermediate value inside a HashSet, Floyd's algorithm stores only two integer variables:

  • slow
  • fast

As a result:

Method Space Complexity
HashSet O(n)
Floyd's Cycle Detection O(1)

Floyd's algorithm achieves the same correctness while using constant memory, making it the preferred approach when memory efficiency is important.