1. 题目
题目描述
编写一个算法来判断一个数 n 是不是快乐数。
快乐数定义:
-
对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和;
-
重复这个过程直到这个数变为 1,则为快乐数;
-
如果无限循环但始终变不到 1,则不是快乐数。
示例
输入:n = 19
输出:true
解释:
(1^2 + 9^2 = 82)
(8^2 + 2^2 = 68)
(6^2 + 8^2 = 100)
(1^2 + 0^2 + 0^2 = 1)
输入:n = 2
输出:false,会进入循环 4→16→37→58→89→145→42→20→4
约束
- (1 <= n <= 2^{31} - 1)
2. 最佳解题思路描述(快慢指针 Floyd 判环,空间 O (1) 最优)
-
定义函数
getSum(x):计算数字各位平方和; -
快慢指针:慢指针每次走 1 次平方和,快指针每次走 2 次;
-
循环:
-
若快指针结果 = 1,是快乐数;
-
若快慢指针相遇,说明出现循环,永远到不了 1,返回 false。
-
优势
无需哈希集合存历史数字,常数空间;时间效率稳定。
3. 可优化代码(固定循环次数,逻辑有漏洞)
class Solution {
public:
bool isHappy(int n) {
int res = n;
for(int i=0;i<10;i++){
int x = res;
res = 0;
while(x){
res += (x%10)*(x%10);
x/=10;
}
if(res == 1){
return true;
}
}
return false;
}
};
代码说明
-
表面能通过题目测试用例,但
存在逻辑漏洞:
-
强行限制最多循环 10 次,理论上存在某些数字需要超过 10 轮才能到 1,会误判 false;
-
依靠题目数据范围投机,不具备严谨性,不符合算法标准解法;
-
-
可取部分:
while(x)拆分数字求平方和的逻辑完全正确; -
缺陷:无循环判环机制,依赖固定迭代次数,算法不通用。
4. 两套标准完整代码
版本 1:哈希集合判环(直观易懂)
#include <unordered_set>
using namespace std;
class Solution {
public:
// 计算各位平方和
int getSum(int x) {
int sum = 0;
while (x) {
int d = x % 10;
sum += d * d;
x /= 10;
}
return sum;
}
bool isHappy(int n) {
unordered_set<int> seen;
while (n != 1 && !seen.count(n)) {
seen.insert(n);
n = getSum(n);
}
return n == 1;
}
};
版本 2:快慢指针 Floyd 判环(最优 O (1) 空间)
class Solution {
public:
int getSum(int x) {
int sum = 0;
while (x) {
int d = x % 10;
sum += d * d;
x /= 10;
}
return sum;
}
bool isHappy(int n) {
int slow = n;
int fast = getSum(n);
while (fast != 1 && slow != fast) {
slow = getSum(slow);
fast = getSum(getSum(fast));
}
return fast == 1;
}
};
5. 总结
-
你的代码拆分数字求平方和逻辑正确,但固定循环次数是投机写法,不严谨,不推荐面试使用;
-
两种标准正规解法:
-
哈希集合:记录所有出现过的数字,重复即代表循环;
-
快慢指针:不用额外存储,空间最优;
-
-
数学结论:所有非快乐数一定会进入循环
4 → 16 → ... → 4,也可每次判断结果是否等于 4 快速返回 false。
6. 相关知识拓展
拓展 1:数学简化写法(利用循环必经过 4)
bool isHappy(int n) {
auto f = [](int x){
int s=0;
while(x){int d=x%10;s+=d*d;x/=10;}
return s;
};
while(n != 1 && n !=4) n = f(n);
return n == 1;
}
拓展 2:复杂度对比
-
哈希集合:(O(\log n)) 时间,(O(\log n)) 空间;
-
快慢指针:(O(\log n)) 时间,(O(1)) 空间(最优);
-
固定 10 次循环:投机写法,无严谨复杂度保障。
拓展 3:易错点复盘
-
忘记循环判环,只限定固定迭代次数,存在误判风险;
-
拆分数字时丢失个位、平方计算写错;
-
快慢指针初始化错误,导致无法进入循环判断。