快乐数是指对正整数不断替换为各位数字平方和后最终得1的数;非快乐数必进入以4为起点的固定循环,因此只需判断n是否等于1或4即可高效判定。

什么是快乐数?先看判定逻辑本质
快乐数的定义是:对一个正整数不断替换为它各位数字的平方和,最终结果为 1,则为快乐数;否则会陷入循环(比如进入 4 → 16 → 37 → 58 → 89 → 145 → 42 → 20 → 4 的死链),永远到不了 1。
关键点在于:所有非快乐数最终都会掉进以 4 为起点的固定循环。这不是巧合,而是数学上可证的——对任意正整数反复计算平方和,其值很快会坍缩到 1~162 范围内(因为三位数最大是 999 → 9²+9²+9² = 243,四位数反而更小),而在这个范围内,只有 1 和 4 是“不动点或循环入口”:1 自身收敛,4 必然导向上述 8 数循环。
所以极速判定不靠哈希表记路径,而靠硬编码检测是否落入 4 循环。
怎么写一个 O(1) 空间 + 极速收敛的判断函数
核心思路:不用 std::unordered_set 存访问过的数,改用 Floyd 判圈(快慢指针)或直接检测是否等于 4 ——后者更轻量、实测更快。
bool isHappy(int n) {
while (n != 1 && n != 4) {
int next = 0;
while (n) {
int d = n % 10;
next += d * d;
n /= 10;
}
n = next;
}
return n == 1;
}
-
n == 4是终止条件,不是启发式猜测:所有非快乐数必经4或直接等于4 - 每轮计算平方和时,用
n % 10和n / 10拆位,比转字符串快一个数量级 - 不需要额外容器,空间复杂度严格
O(1) - 实测对 10⁹ 内任意数,最多 20 轮就收敛(因数值快速衰减)
为什么不能只判 n == 1 就返回 true?
常见错误写法:
while (n != 1) { /* 计算 next */ } return true;
这会无限循环——比如输入 2:2 → 4 → 16 → 37 → ... → 4 → …… 永远卡住。
必须显式截断非快乐路径。可选方案有:
- 判
n == 4(最简,数学保证安全) - 判
n (因为个位数中只有 1 和 7 是快乐数,但需额外查表) - 用快慢指针检测循环(通用但稍重,要维护两个变量)
4 是唯一需要拦截的“最小坏种子”,其他如 16、37 都会再算几轮回到 4,所以只拦 4 就够了。
边界与性能注意点
- 输入保证是正整数,无需检查
-
INT_MAX 是 2147483647,其平方和为 2²+1²+4²+7²+4²+8²+3²+6²+4²+7² = 236,远小于原值,所以不会溢出 int
- 若你用
long long 做中间变量反而拖慢:现代 CPU 对 int 运算有更好流水线支持
- 多次调用场景下,可预计算 1~243 内所有数的快乐性打表,但单次判断没必要
INT_MAX 是 2147483647,其平方和为 2²+1²+4²+7²+4²+8²+3²+6²+4²+7² = 236,远小于原值,所以不会溢出 int
long long 做中间变量反而拖慢:现代 CPU 对 int 运算有更好流水线支持真正容易被忽略的是:别试图用 set 记录历史值来“保险”——那在嵌入式或高频调用场景下会触发内存分配,且 cache 局部性差;而 n == 4 分支几乎总命中 L1 cache,才是真正的极速路径。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











