题目链接

题目描述

编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」定义为:

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

如果 n 是快乐数就返回 true ;不是,则返回 false

示例

示例1:

输入:19 输出:true 解释: 1 + 9 = 82 8 + 2 = 68 6 + 8 = 100 1 + 0 + 0 = 1

提示

  • 1 <= n <= 2 - 1

    思路

    本题看似是一道数学题,但其实想要直接证明一个数是否是快乐数是比较困难的。仔细阅读题目,发现计算过程是可能死循环的,死循环的原因其实是产生了环。
    在循环计算快乐数的过程中,考虑所有情况:
  1. 最终得到1
  2. 值越来越大,直到无穷大
  3. 进入死循环

其中,情况2是我们不想看到的,因为它很难处理,事实上,这种情况是不存在的,证明如下:
最大的 0202-快乐数 - 图1 位数为 0202-快乐数 - 图2, 其快乐数为 0202-快乐数 - 图3
最大的 0202-快乐数 - 图4 位数为 0202-快乐数 - 图5, 其快乐数为 0202-快乐数 - 图6
最大的 0202-快乐数 - 图7 位数为 0202-快乐数 - 图8 , 其快乐数为 0202-快乐数 - 图9
0202-快乐数 - 图100202-快乐数 - 图11
即当一个数大于等于4位时,它的快乐数是不断变小的,直到变为三位数,且三位数的快乐数不大于243,由此就可以排除情况2。
因此,本题的关键是判断是否有环

思路1:哈希表(哈西集合)

如果一个数不在哈西集合里,就添加它;如果在集合中,说明存在环。

思路2:快慢指针

类似判断链表是否有环,我们同样可以使用快慢指针判断。

思路3:数学

由之前的分析我们可知,对于输入的数来说,它的快乐数最终都会收敛到243以内,因此,若有环,也是在243内循环;
对于243这种较小的数字,我们可以编程寻找环。
最后得出只存在 0202-快乐数 - 图12 。因此,我们可以判断输入的数最终的快乐数是否落在这个环里即可。

题解

思路1:哈希表(哈西集合)

  1. class Solution {
  2. private:
  3. int getSum(int n) {
  4. int sum = 0;
  5. while (n) {
  6. sum += (n % 10) * (n % 10);
  7. n /= 10;
  8. }
  9. return sum;
  10. }
  11. public:
  12. bool isHappy(int n) {
  13. unordered_set<int> visited;
  14. while (n != 1 && visited.find(n) == visited.end()) {
  15. visited.insert(n);
  16. n = getSum(n);
  17. }
  18. return 1 == n;
  19. }
  20. };

思路2:快慢指针

class Solution {
   private:
    int getSum(int n) {
        int sum = 0;
        while (n) {
            sum += (n % 10) * (n % 10);
            n /= 10;
        }
        return sum;
    }

   public:
    bool isHappy(int n) {
        int slow = n;
        int fast = getSum(n);

        while (n != 1 && slow != fast) {
            slow = getSum(slow);
            fast = getSum(getSum(fast));
        }

        return 1 == fast;
    }
};

思路3:数学

class Solution {
   private:
    unordered_set<int> cycleNum{4, 16, 37, 58, 89, 145, 42, 20};
    int getSum(int n) {
        int sum = 0;
        while (n) {
            sum += (n % 10) * (n % 10);
            n /= 10;
        }
        return sum;
    }

   public:
    bool isHappy(int n) {
        while (n != 1 && cycleNum.find(n) == cycleNum.end()) {
            n = getSum(n);
        }

        return 1 == n;
    }
};

复杂度分析

思路1:哈希表(哈西集合)

  • 时间复杂度:0202-快乐数 - 图13
  • 空间复杂度:0202-快乐数 - 图14,对于大于243的数我们可以不保存它,因为无论什么样的数,最终都会落到243以内。

    思路2:快慢指针

  • 时间复杂度:0202-快乐数 - 图15

  • 空间复杂度:0202-快乐数 - 图16

    思路3:数学

  • 时间复杂度:0202-快乐数 - 图17

  • 空间复杂度:0202-快乐数 - 图18