一、题目描述
题目链接:
https://leetcode.cn/problems/happy-number/
编写一个算法来判断一个数 n 是不是 快乐数。
快乐数定义:
对于一个正整数:
-
每一次将该数替换为 各位数字的平方和
-
然后重复这个过程
-
直到结果变为
1 -
或者 进入无限循环
如果最终结果为 1,则该数为 快乐数。
示例 1
输入:n = 19
输出:true
过程:
1² + 9² = 82
8² + 2² = 68
6² + 8² = 100
1² + 0² + 0² = 1
最终得到 1,所以 19 是快乐数。
示例 2
输入:n = 2
输出:false
提示
1 <= n <= 2^31 - 1
二、题目核心思想
本题核心操作:
不断计算:
各位数字平方和
例如:
19
↓
1² + 9² = 82
↓
8² + 2² = 68
↓
6² + 8² = 100
↓
1² + 0² + 0² = 1
如果:
最终 == 1 → 快乐数
进入循环 → 不是快乐数
因此问题本质就是:
如何检测是否进入循环
三、辅助函数:计算平方和
无论哪种解法,都需要一个函数计算:
各位数字平方和
int getNext(int n)
{
int sum = 0;
while (n)
{
int digit = n % 10;
sum += digit * digit;
n /= 10;
}
return sum;
}
四、解法一:暴力循环(基础版)
思路
不断计算平方和:
n → next → next → next ...
如果:
n == 1
说明是快乐数。
但这种写法 无法判断循环,不推荐。
五、解法二:哈希表判重
思路
如果一个数不是快乐数,一定会 进入循环。
例如:
2
↓
4
↓
16
↓
37
↓
58
↓
89
↓
145
↓
42
↓
20
↓
4 (出现重复)
因此我们可以:
用哈希表记录出现过的数字
如果:
再次出现 → 说明进入循环
算法步骤
1 初始化哈希表
2 不断计算平方和
3 如果出现1 → 返回true
4 如果出现重复 → 返回false
C语言实现
#include <stdbool.h>
int getNext(int n)
{
int sum = 0;
while (n)
{
int digit = n % 10;
sum += digit * digit;
n /= 10;
}
return sum;
}
bool isHappy(int n)
{
int seen[1000] = {0};
while (n != 1 && !seen[n])
{
seen[n] = 1;
n = getNext(n);
}
return n == 1;
}
复杂度分析
时间复杂度:O(log n)
空间复杂度:O(n)
六、解法三:快慢指针(最优解)
这是 面试最推荐的写法。
思想来源:
Floyd 判环算法
类似:
判断链表是否有环
思路
定义:
slow 每次走一步
fast 每次走两步
如果:
fast == 1
说明是快乐数。
如果:
slow == fast
说明进入循环。
图解
slow : n → next → next
fast : n → next → next → next → next
若存在循环:
slow == fast
C语言实现
#include <stdbool.h>
int getNext(int n)
{
int sum = 0;
while (n)
{
int digit = n % 10;
sum += digit * digit;
n /= 10;
}
return sum;
}
bool isHappy(int n)
{
int slow = n;
int fast = getNext(n);
while (fast != 1 && slow != fast)
{
slow = getNext(slow);
fast = getNext(getNext(fast));
}
return fast == 1;
}
复杂度分析
时间复杂度:O(log n)
空间复杂度:O(1)
优于哈希表方法。
七、为什么一定会进入循环?
数学证明表明:
任何数字经过若干次计算后:
最终都会落到 1000 以内
如果不是快乐数,会进入固定循环:
4 → 16 → 37 → 58 → 89 → 145 → 42 → 20 → 4
因此:
出现4基本可以判断不是快乐数
有些代码会直接判断 n != 4。
八、面试总结
本题本质是:
循环检测问题
常见解法:
| 解法 | 思想 | 空间 |
|---|---|---|
| 暴力循环 | 不推荐 | - |
| 哈希表 | 判重 | O(n) |
| 快慢指针 | Floyd判环 | O(1) |
面试建议:
优先写:快慢指针
因为:
空间复杂度 O(1)
九、相关题目推荐
LeetCode 中类似思想题:
141. 环形链表
142. 环形链表 II
202. 快乐数
287. 寻找重复数
核心都是:
Floyd 判环算法