题目描述:
题目来源:力扣「快乐数」
题目描述:
编写一个算法来判断一个数 n 是不是快乐数。
**「快乐数」**定义为:
- 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
- 然后重复这个过程直到这个数变为 1,也可能是无限循环但始终变不到 1。
- 如果这个过程结果为1,那么这个数就是快乐数。
如果 n 是快乐数 就返回 true;不是,则返回 false。

示例 1: 输入 n = 19,返回 true。计算过程如下:
- 1² + 9² = 82
- 8² + 2² = 68
- 6² + 8² = 100
- 1² + 0² + 0² = 1
最终结果为 1,因此 19 是快乐数。
示例 2: 输入 n = 2,返回 false。计算过程如下:
- 2² = 4
- 4² = 16
- 1² + 6² = 37
- 3² + 7² = 58
- 5² + 8² = 89
- 8² + 9² = 145
- 1² + 4² + 5² = 42
- 4² + 2² = 20
- 2² + 0² = 4
可以看到,数值进入 4 → 16 → 37 → 58 → 89 → 145 → 42 → 20 → 4 的循环,始终无法变为 1,因此 2 不是快乐数。
边界情况: 当 n = 1 时,1² = 1,直接满足条件,无需任何迭代即可返回 true。
算法原理:
类比判断链表是否有环
解法:快慢双指针
1.定义快慢双指针
2.慢指针每次移动一步快指针每次移动两步
3.判断相遇的数的值即可。值为1则为快乐数
双指针仅仅是一个思想,我们可以用数组的下标充当指针,某一个数充当一个指针。



cpp
class Solution
{
int clsum(int n)//返回n这个数的每一位的平方和
{
int sum =0;
while(n)
{
int t =n%10; //
sum+=t*t;
n/=10;
}
return sum;
}
public:
bool isHappy(int n)
{
int slow =n;
int fast =clsum(n);
while(slow!=fast)
{
slow=clsum(slow); //slow走一步
fast =clsum(clsum(fast));//fast走两步
}
return fast==1;
}
};
复杂度分析:
该快慢双指针解法的时间复杂度为 O(log n),空间复杂度为 O(1)。
为什么时间复杂度是 O(log n) 而不是 O(n)?关键在于 clsum 函数对每个数求各位数字平方和的过程。对于一个 k 位数,clsum 需要执行 k 次循环,而 k = log10(n),因此单次计算平方和的代价是 O(log n)。
更重要的是,快乐数序列中的数值会迅速缩小。以最大的 10 位数 9999999999 为例,其各位平方和为 9² × 10 = 810,也就是说,无论初始的 n 有多大,经过一次 clsum 之后,数值都会立刻降到 810 以下。此后所有参与计算的数都落在 [1, 810] 这个有限区间内,快慢指针最多在这个区间内循环若干次就会相遇,循环次数是一个与 n 的位数无关的常数。
因此,总时间复杂度 = 常数次循环 × 每次 O(log n) 的平方和计算 = O(log n)。如果误以为是 O(n),通常是忽略了「平方和运算会迅速压缩数值范围」这一关键性质,误把循环次数当成了与 n 成正比。