A number is "happy" if repeatedly replacing it with the sum of its squared digits eventually reaches 1. The trick is recognising that unhappy numbers always fall into a cycle— detect it with a seen-set or Floyd's fast/slow pointers and you're done.
Write a function that returns true if a positive integer n is a happy number. Starting from n, repeatedly replace it with the sum of the squares of its digits. If the process eventually produces 1, the number is happy; otherwise it cycles forever and we return false.
Worked example — n = 19:
82681001 ✓Result: true. For n = 2 the sequence eventually hits 4 → 16 → … → 4 — a cycle, so false.
1 (happy) or enters an infinite loop (unhappy). Unhappy numbers always pass through 4, then repeat the same sequence. This means cycle detection is the whole problem. Store each value in a Set; if you see a value twice before reaching 1, you're in a cycle → return false.Set<number>. This is your cycle detector.1 (happy).n, test seen.has(n). If the value already appeared, we've entered a cycle → return false.n to seen, then set n = sumOfSquaredDigits(n).n === 1 — the number is happy.n to seen before advancing, or they check seen.has(n) after advancing — then the set stores next-step values and the cycle check never fires on the right value. Always: check → add → advance.The seen-set approach uses O(log n) space (the number of unique values before a cycle is bounded by the size of the value, which shrinks to at most 3 digits quickly). For true O(1) space, use Floyd's cycle detection: slow takes one step, fast takes two. If they ever meet at a value other than 1, it's a cycle. If fast reaches 1, it's happy.
19. We'll repeatedly replace n with the sum of squared digits until we reach 1 (happy) or revisit a number (cycle → unhappy).1▶function isHappy(n: number): boolean {2▶ const seen = new Set<number>();3 while (n !== 1) {4 if (seen.has(n)) return false; // cycle detected5 seen.add(n);6 n = sumOfSquaredDigits(n);7 }8 return true;9}1011function sumOfSquaredDigits(n: number): number {12 let sum = 0;13 while (n > 0) {14 const d = n % 10;15 sum += d * d;16 n = Math.floor(n / 10);17 }18 return sum;19}
function isHappy(n: number): boolean {
const seen = new Set<number>();
while (n !== 1) {
if (seen.has(n)) return false; // cycle detected
seen.add(n);
n = sumOfSquaredDigits(n);
}
return true;
}
function sumOfSquaredDigits(n: number): number {
let sum = 0;
while (n > 0) {
const d = n % 10;
sum += d * d;
n = Math.floor(n / 10);
}
return sum;
}Set<number>stores every value we've seen. Once a value recurs we know we're looping. Without this, the process for unhappy numbers never terminates.n !== 1. Inside: check the set first (cycle → false), then add n, then advance. This exact order prevents the classic off-by-one where you add after advancing and miss the cycle.n === 1, so we know the sequence reached the happy ending.n % 10, square it, add to sum, then floor-divide to discard that digit. This is O(log n) — one iteration per digit.sumOfSquaredDigits is O(log n) (one step per digit). For a starting number n, the value drops to at most 3 digits after the first step, so the chain length before hitting 1 or a cycle is bounded by a constant for any practical input — effectively O(log n) total time. The seen-set uses O(log n) space; Floyd's variant uses O(1).slow = step(n), fast = step(step(n)), advance until they meet or fast === 1.4 → 16 → 37 → 58 → 89 → 145 → 42 → 20 → 4 → … This is provable; knowing it lets you hard-code a fast exit.1 returns true immediately; 19 is the smallest two-digit happy number; 2 is the smallest unhappy number.| "repeatedly apply a function until a condition" | seen-set cycle detection |
| "infinite loop in a finite domain" | Floyd's fast/slow pointers |
| "sum of digit squares" | extract digits with % 10, floor / 10 |
| "does the process terminate or cycle?" | seen-set or Floyd on the sequence |
function isHappy(n: number): boolean {
const seen = new Set<number>();
while (n !== 1) {
if (seen.has(n)) return false;
seen.add(n);
n = sumOfSquaredDigits(n);
}
return true;
}
function sumOfSquaredDigits(n: number): number {
let sum = 0;
while (n > 0) {
const d = n % 10;
sum += d * d;
n = Math.floor(n / 10);
}
return sum;
}n to the seen-set before computing the next value?sumOfSquaredDigits(13) return?