快乐数是指正整数经各位数字平方和迭代后最终为1的数;判断逻辑为:用unordered_set记录已出现数值,每次计算平方和前插入当前值,若遇重复或结果为1则终止,返回是否等于1。

什么是快乐数,以及判断逻辑怎么写
快乐数的定义很明确:对一个正整数不断替换为它各位数字的平方和,最终结果要么是 1(快乐),要么陷入循环(不快乐)。关键不是“算多少轮”,而是检测是否进入循环——一旦重复出现某个中间值,就说明不可能到 1 了。
所以核心操作就两步:sumOfSquares() 计算平方和,再用集合(如 std::unordered_set)记录已出现的数。每次计算后检查是否为 1 或已在集合中。
常见错误是只判断是否等于 1 就返回 true,却没处理循环;或者误以为所有非 1 结果都会发散(实际会快速收敛进小范围循环)。
如何高效实现 sumOfSquares()
这个函数要拆出每位数字并累加平方,别用字符串转换(开销大),也别用 log10 求位数(浮点误差风险)。直接用取模和整除最稳妥:
立即学习“C++免费学习笔记(深入)”;
int sumOfSquares(int n) {
int s = 0;
while (n) {
int d = n % 10;
s += d * d;
n /= 10;
}
return s;
}注意点:
-
n是正整数,无需处理负数或零(题目限定) - 用
int足够:最大输入比如999999999→ 平方和最多9*81=729,后续只会更小 - 别写成
d = n % 10; n = n / 10;后漏掉d*d累加——这是新手常漏的一步
用 std::unordered_set 检测循环的注意事项
集合用来存历史值,只要当前值已存在,立刻返回 false。这里几个实际细节容易被忽略:
- 必须在计算新值 前 插入当前值,否则第一次的
1会被跳过检测 - 插入失败(即已存在)时,不要继续循环,立刻返回 false
- 别用
std::set——没必要排序,unordered_set平均 O(1) 查找更合适 - 如果编译器较老(C++11 之前),需确认是否支持
unordered_set;否则退化用std::set,性能略差但逻辑不变
典型结构:
std::unordered_set<int> seen;
while (n != 1 && seen.find(n) == seen.end()) {
seen.insert(n);
n = sumOfSquares(n);
}
return n == 1;边界与性能:为什么不用快慢指针也能稳过
有人提“用 Floyd 判圈法(快慢指针)更省内存”,理论上没错,但对快乐数来说没必要:
- 所有不快乐数最终都会进入同一个循环:
4 → 16 → 37 → 58 → 89 → 145 → 42 → 20 → 4,长度仅 8 - 最大中间值不超过
730,unordered_set最多存几十个数,内存开销可忽略 - 快慢指针要额外写两套移动逻辑,反而增加出错概率(比如慢指针走一次、快指针走两次的顺序搞反)
- LeetCode 上该题用 set 的解法平均耗时比快慢指针还略优,因为哈希查找在小数据集上非常快
真正要注意的是:别把 sumOfSquares() 写进循环里反复声明临时变量(比如每次 new vector 存各位),这种隐藏开销比 set 本身更大。


















