快乐数是指对正整数反复计算各位数字平方和,最终结果为1的数;若进入循环且不等于1(如4→16→…→4),则不是快乐数。常用unordered_set检测重复值,或用快慢指针判环,时间O(log n),空间O(1)或O(log n)。

什么是快乐数?先看判定逻辑
快乐数的定义很明确:对一个正整数反复执行「各位数字平方和」操作,最终结果为 1 就是快乐数;如果进入循环且始终不等于 1(比如卡在 4 → 16 → 37 → 58 → 89 → 145 → 42 → 20 → 4 这个环),就不是快乐数。
关键不是算多少轮,而是判断是否出现重复值——一旦重复,说明已进环,不可能到 1。
用 unordered_set 检测循环最直接
每次计算出新值,存进 std::unordered_set<int></int>;如果新值已存在,立刻返回 false;如果等于 1,返回 true。
注意点:
立即学习“C++免费学习笔记(深入)”;
-
unordered_set查找和插入平均 O(1),比set(O(log n))更合适 - 别忘了把初始数也 insert 进去,否则第一次重复可能漏判
- 计算平方和时,用
n % 10和n / 10拆位,避免转字符串(开销大、易错)
int next(int n) {
int sum = 0;
while (n) {
int d = n % 10;
sum += d * d;
n /= 10;
}
return sum;
}
<p>bool isHappy(int n) {
std::unordered_set<int> seen;
while (n != 1 && seen.find(n) == seen.end()) {
seen.insert(n);
n = next(n);
}
return n == 1;
}不用额外空间?快慢指针也能判环
把「平方和变换」看作链表的 next 指针,问题就变成检测链表是否有环——这时可用 Floyd 判圈算法(快慢指针)。
优势:空间复杂度降到 O(1);劣势:代码稍长,理解成本略高。
实操要点:
- 慢指针走一步:
slow = next(slow);快指针走两步:fast = next(next(fast)) - 循环条件是
slow != fast,退出后检查slow == 1即可 - 必须确保
next()函数无副作用、纯函数式,否则快慢不同步
注意:不是所有输入都会进固定环,但数学上已证明,非快乐数最终必落入 4 开头的环,所以快慢指针一定相遇。
边界和性能容易踩的坑
常见错误现象:isHappy(1) 返回 false、isHappy(10) 卡死、或对 INT_MAX 类输入溢出。
规避方式:
-
next()中用int足够:最大输入是999999999(9 个 9),其平方和为9*9*9 = 729,远小于INT_MAX - 别在循环里反复 new/delete 或构造 string——高频调用时明显拖慢
- C++11 后推荐用
std::unordered_set而非std::set,尤其数据量小时哈希表常数更优 - 如果批量判断(如筛 1~1000),可预计算打表,但单次调用没必要
真正麻烦的是误判循环起点——比如把 seen.insert(n) 放在 while 循环末尾,会导致第一次 n==1 时没存进去,下次再进来就漏了判断。这个细节,调试时得盯住入口和出口的顺序。


















