快乐数判断关键在于检测循环而非仅判断是否等于1,因非快乐数会陷入如4→16→37→…→4的循环;需用unordered_set记录已出现数字或快慢指针判圈,并在next计算中用取模避免字符串转换和pow精度问题。

快乐数的判断关键在于识别循环——一旦数字进入重复序列,就不是快乐数;如果最终变成 1,就是快乐数。核心不是算多少次,而是防止无限循环。
为什么不能只看是否等于 1?
因为非快乐数会陷入循环(比如 4 → 16 → 37 → 58 → 89 → 145 → 42 → 20 → 4),永远到不了 1。直接 while (n != 1) 会死循环。
常见错误现象:runtime error: signed integer overflow 或程序卡住 —— 实际是进入了已知的非快乐数循环,但没检测。
- 必须记录已出现过的数字,发现重复就立刻返回
false - 用
std::unordered_set是最直接的方式,插入和查找平均 O(1) - 也可以用快慢指针(Floyd 判圈),避免额外空间,但逻辑稍绕,初学者易写错 next() 函数
如何正确计算各位平方和?
别用字符串转换(如 to_string + 遍历),效率低且引入不必要的内存分配。整数取模更轻量、更符合 C++ 习惯。
实操建议:
- 用
n % 10取个位,n /= 10去掉个位,循环直到n == 0 - 注意:
0的平方和是0,但题目限定输入为正整数,所以初始n > 0,无需特判 0 - 避免写成
pow(digit, 2)——pow返回double,有精度风险,且比digit * digit慢
示例片段:
int next(int n) {
int sum = 0;
while (n) {
int d = n % 10;
sum += d * d;
n /= 10;
}
return sum;
}
用 unordered_set 检测循环的典型写法
这是最直观、出错率最低的实现方式,适合绝大多数场景。
- 每次计算新值前,先检查是否已在
seen中;若在,返回false - 若新值为
1,直接返回true -
seen.insert(n)放在判断之后、更新之前,确保不漏掉当前值 - 不要把
1插入集合再 break ——1是终止态,不应被当作循环点
关键顺序不能颠倒:
while (n != 1) {
if (seen.find(n) != seen.end()) return false;
seen.insert(n);
n = next(n);
}
return true;
快慢指针虽省空间,但需保证 next() 正确性;而 unordered_set 对小数字(快乐数通常收敛很快)内存开销几乎可忽略。真正容易被忽略的是:很多实现漏了对中间值 1 的即时返回,导致多算一轮甚至误判。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











