快乐数是指对正整数反复计算各位数字平方和,最终结果为1的数;若陷入非1循环(如4→16→37→…→4)则不是。核心是检测是否进入循环,常用unordered_set判重或快慢指针法,预计算可优化高频查询。

什么是快乐数?先看判定逻辑本质
快乐数的定义是:对一个正整数反复执行「各位数字平方和」操作,最终结果为 1,则它是快乐数;若进入非 1 的循环(比如 4 → 16 → 37 → 58 → 89 → 145 → 42 → 20 → 4),就不是。
关键不在“算多少轮”,而在于是否陷入循环。暴力模拟可能无限跑下去,必须检测重复状态。
用 unordered_set 检测循环最直接
这是最直观、不易出错的做法:把每次算出的和存进 unordered_set,遇到重复值就说明循环了。
常见错误现象:
- 用
vector或set存历史值 → 插入/查找变慢,影响性能 - 忘记在循环开始前插入初始值 → 第一次重复判断失效
- 把 1 当作“循环起点”误判为不快乐 → 实际上到达 1 就该立即返回 true
使用场景:适合教学、调试、或对性能要求不极端的场合。
实操建议:
- 初始化
unordered_set<int></int>,插入原始数 - 循环体里先算下一轮和(
next),再检查是否为 1 或已存在 - 若
next == 1,立刻返回 true;若seen.count(next)为真,返回 false - 否则插入
next,继续
int getNext(int n) {
int sum = 0;
while (n) {
int d = n % 10;
sum += d * d;
n /= 10;
}
return sum;
}
<p>bool isHappy(int n) {
unordered_set<int> seen;
while (n != 1 && !seen.count(n)) {
seen.insert(n);
n = getNext(n);
}
return n == 1;
}</int></p>
快慢指针法:O(1) 空间,但容易写错边界
这是 Floyd 判圈算法的典型应用——把「数字变换」看作链表,用两个指针模拟龟兔赛跑。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
容易踩的坑:
- 忘记快指针要走两步(即调用两次
getNext),只走一步就退化成普通遍历 - 初始时快慢指针都设为
n,导致第一次比较恒等 → 必须让快指针先走一步(或初始化为getNext(n)) - 没处理好 1 的提前退出:若慢指针到达 1,应立刻返回 true,不能等快慢相遇
参数差异:相比哈希表法,空间从 O(k) 降到 O(1),k 是路径长度;时间复杂度仍是 O(k),但常数略大(因快指针多算一倍)。
实操建议:
- 慢指针每次走一步:
slow = getNext(slow) - 快指针每次走两步:
fast = getNext(getNext(fast)) - 循环条件用
slow != fast,但需在每次移动后检查slow == 1或fast == 1 - 相遇时若值为 1 → 是快乐数;否则不是
预计算小范围结果可加速高频查询
如果你的应用中要频繁判断 1~1000 内的数(比如做题、打表、游戏逻辑),直接查表比实时计算更快。
性能影响:
- 首次构建表有开销,但之后是 O(1) 查询
-
isHappy[1001]数组仅占 1KB,无内存压力 - 注意:表必须覆盖所有中间可能出现的值(如 19 → 82 → 68 → 100 → 1),所以实际要算到最大可能中间值(对 ≤1000 的输入,最大中间和是 9²×3 = 243,再算几轮就能收敛)
实操建议:
- 用静态数组或
vector<bool></bool>预填 0~243 或 0~1000 范围 - 用上面任一方法(推荐哈希版)批量初始化
- 查询时直接
return precomputed[n]
真正难的不是算法本身,而是想清楚:你到底在判断「能否抵达 1」,还是「会不会卡在某个循环里」——后者才是所有实现绕不开的核心。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










