快乐数是反复将各位数字平方和相加最终得1的正整数;非快乐数必落入4→16→37→58→89→145→42→20→4循环,可用快慢指针o(1)空间检测环。

什么是快乐数?先看判定逻辑
快乐数的定义是:对一个正整数反复做「各位数字平方和」运算,最终结果为 1,则它是快乐数;如果进入循环且永远到不了 1,就不是快乐数。关键不是算多少轮,而是判断是否陷入循环——这本质上是个环检测问题。
用快慢指针检测循环(推荐解法)
暴力存 visited 集合虽直观,但额外空间开销大;而 slow 和 fast 两个指针模拟龟兔赛跑,空间 O(1),且天然适配这种“函数迭代产生链表”的场景。
-
nextNumber()函数必须正确实现:取模求个位、整除去个位,别漏掉最后一位(常见错误是循环条件写成n > 0却在循环内没更新n) - 初始时
slow = n,fast = nextNumber(n),避免一开始就相等导致提前退出 - 循环条件用
slow != fast,内部每次更新:slow = nextNumber(slow),fast = nextNumber(nextNumber(fast)) - 跳出循环后,只需检查
slow == 1即可,不用再判fast
手动模拟时容易卡在哪几步?
手算验证时,常因中间值算错导致误判。比如 19:1²+9²=82 → 8²+2²=68 → 6²+8²=100 → 1²+0²+0²=1,是快乐数;但有人把 68 算成 6²+8=44,就全错了。
- 务必逐位取数字:用
n % 10取末位,n / 10整除去末位,别用字符串转换(虽可行但绕远且易出边界) - 注意整型溢出风险:输入最大是
INT_MAX(约 2e9),其各位平方和最大为 9²×10 = 810,完全不会溢出,无需 long - 不要提前终止:即使某次得到 1,也要确保是最终稳定态——但按定义,1 的下一轮还是 1,所以只要出现 1 就可返回 true
为什么不能只看是否重复出现某个数?
单纯记录已见过的数(如 unordered_set)也能判循环,但快慢指针更省空间。不过要注意:不是所有中间值都需记录,比如 2→4→16→37→58→89→145→42→20→4,这里 4 第二次出现才成环,但第一次出现时无法预知。
- 用 set 判重时,插入前先查是否存在,存在即环,返回 false;等于 1 时返回 true
- 快慢指针中,
fast走两步、slow走一步,只要存在环,二者必相遇——这是 Floyd 算法保证的,不依赖具体数值分布 - 两者时间复杂度都是 O(log n) 级别(因为数字位数有限,平方和会快速收敛到三位数以内)
实际写 nextNumber 时,最简实现就是 while 循环加 sum += digit * digit,别试图用 log10 或 to_string——多此一举还容易出 bug。判定的核心不在数学技巧,而在正确建模「迭代序列是否有环」这个事实。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











