📚 目录
-
[1. 题目解析](#1. 题目解析)
-
[2. 算法原理](#2. 算法原理)
-
[3. 编写代码](#3. 编写代码)
今天我们来做LeetCode上的经典题目---202. 快乐数
1. 题目解析

解决这道题,常见有两种思路:
- 哈希集合判重:用
HashSet记录迭代过程中出现的数字,出现重复就说明有环。优点是逻辑直观,缺点是需要额外的O(log n)空间。 - 快慢指针判环:利用快慢指针相遇来判断环的存在,空间复杂度O(1),也是我们双指针专题的核心考点。
此时,我们今天解这道题用的是:双指针->快慢指针.
[🔙 返回目录](#🔙 返回目录)
2. 算法原理

此时我们还遇到了一种特殊情况:这个数字呈现直线 的情况;
这里我们要明确一点:本题中,数字的迭代路径不可能一直呈直线,所有数最终都会进入循环。
原因有两点:
- 鸽巢原理的限制 :无论初始数字多大 ,每次求各位平方和时,数字都会快速缩小,最终落入 一个有限的数字集合 中。根据鸽巢原理 ,迭代次数超过集合大小后,必然会出现重复数字,从而进入循环。
- 快乐数的收敛特性:如果数字是快乐数,它最终会变成1,而1的平方和永远是1,会形成一个值全为1的自循环,而非一直延伸的直线。
因此,我们只需要判断迭代路径是否进入循环,并且判断循环中是否包含1,就能得出结果。
额外拓展:鸽巢原理:

我们可以结合题目给定的输入范围 1 ≤ n < 2^31 - 1 ,来量化这个有限集合的大小:
2^31-1 是一个10位数 ,其各位全为9时的平方和最大 ,为 10×9²=810 。后续迭代中数字会持续缩小,因此所有迭代值都落在 [1, 810] 这个有限集合中。根据鸽巢原理,迭代次数超过 810次后,必然会出现重复数字 ,进入循环 。
此时,我们特殊情况就不会存在!!!
[🔙 返回目录](#🔙 返回目录)
3. 编写代码
java
class Solution {
//求每一位数字的平方和
public int sumbit(int n) {
int sum = 0;
while(n>0) {
int tmp = n%10;
sum+=tmp*tmp;
n/=10;
}
return sum;
}
public boolean isHappy(int n) {
//定义快慢指针
int slow = n;
//先让快指针先走1步
int fast = sumbit(n);
while(slow!=fast) {
//慢指针走一步
slow = sumbit(slow);
//快指针走两步
fast = sumbit(sumbit(fast));
}
//或者是fast
return slow==1;
}
}

复杂度分析:
- 时间复杂度 :O(log n)。每次迭代中,数字的大小都会快速缩小,且快慢指针相遇的时间与循环的长度成正比,整体复杂度和数字的位数相关。
- 空间复杂度 :O(1)。快慢指针只使用了常数级别的额外空间 ,这也是快慢指针解法相比哈希集合解法的优势。
两种解法对比:
| 解法 | 时间复杂度 | 空间复杂度 | 特点 |
|---|---|---|---|
| 哈希集合判重 | O(log n) | O(log n) | 逻辑直观,容易理解 |
| 快慢指针判环 | O(log n) | O(1) | 空间复杂度最优,是双指针的经典应用 |
总结 :
这道题的核心考点,是将**"数字迭代"问题转化为"链表判环"问题**,并用快慢指针解决。鸽巢原理则 证明了循环必然存在 ,为我们的解法提供了理论支撑。掌握这个思路,以后遇到类似的循环问题,都可以用快慢指针来解决。
[🔙 返回目录](#🔙 返回目录)