🚩传送门: 牛客题目
力扣题目

题目

编写一个算法来判断一个数 [NC]391. 快乐数 - 图1 是不是快乐数。

「快乐数」 定义为:

  • 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
  • 然后重复这个过程直到这个数变为 [NC]391. 快乐数 - 图2,也可能是 无限循环 但始终变不到 [NC]391. 快乐数 - 图3
  • 如果这个过程 结果为[NC]391. 快乐数 - 图4,那么这个数就是快乐数。

如果 [NC]391. 快乐数 - 图5 快乐数 就返回 true ;不是,则返回 false

示例 1:

输入:n = 19 输出:true 解释: 12 + 92 = 82 82 + 22 = 68 62 + 82 = 100 12 + 02 + 02 = 1

解题思路:快慢指针

我们可以先举几个例子。我们从 [NC]391. 快乐数 - 图6 开始。则下一个数字是 [NC]391. 快乐数 - 图7(因为 [NC]391. 快乐数 - 图8),然后下一个数字是 [NC]391. 快乐数 - 图9(因为 [NC]391. 快乐数 - 图10)。我们可以不断重复该的过程,直到我们得到 [NC]391. 快乐数 - 图11。因为我们得到了 [NC]391. 快乐数 - 图12,我们知道数字 [NC]391. 快乐数 - 图13 是一个快乐数,函数应该返回 true
[NC]391. 快乐数 - 图14
再举一个例子,让我们从 [NC]391. 快乐数 - 图15 开始。通过反复通过平方和计算下一个数字,我们最终得到 [NC]391. 快乐数 - 图16,再继续计算之后,我们又回到 [NC]391. 快乐数 - 图17。由于我们回到了一个已经计算过的数字,可以知道有一个循环,因此不可能达到 [NC]391. 快乐数 - 图18。所以对于 [NC]391. 快乐数 - 图19 ,函数应该返回 false
[NC]391. 快乐数 - 图20
根据我们的探索,我们猜测会有以下三种可能。

  1. 最终会得到 [NC]391. 快乐数 - 图21
  2. 最终会进入循环。
  3. 值会越来越大,最后接近无穷大。

第三个情况比较难以检测和处理。为什么无限循环就一定是走到了环了呢 ?而不是链表上的数字越变越大 ?

[NC]391. 快乐数 - 图22[NC]391. 快乐数 - 图23 表示每一位上的数字,并且 [NC]391. 快乐数 - 图24。我们有 [NC]391. 快乐数 - 图25

我们需要证明:[NC]391. 快乐数 - 图26

以下是证明:

[NC]391. 快乐数 - 图27

因此只有两种结果:

  1. 最终会得到 [NC]391. 快乐数 - 图28
  2. 最终会进入循环。

对于链表的环问题,经典解法使用快慢指针

复杂度分析

时间复杂度:[NC]391. 快乐数 - 图29

空间复杂度:[NC]391. 快乐数 - 图30,指针需要常数的额外空间

我的代码

  1. class Solution {
  2. // 寻找下一个数
  3. public int bitSquareSum(int n) {
  4. int sum = 0;
  5. while (n > 0) {
  6. int bit = n % 10;
  7. sum += bit * bit;
  8. n = n / 10;
  9. }
  10. return sum;
  11. }
  12. public boolean isHappy(int n) {
  13. int slow = n, fast = n;
  14. do {
  15. slow = bitSquareSum(slow); // 走1步
  16. fast = bitSquareSum(fast);
  17. fast = bitSquareSum(fast); // 走2步
  18. } while (slow != fast); // 如果有环一定会相交、没环快指针会在1处等待慢指针
  19. return slow == 1;
  20. }
  21. }