题目

题目来源:力扣(LeetCode)

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」定义为:

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

思路分析

根据题意,我们先列举一个例子:
我们从 19 开始,那么下一个数字是 82 (因为 1^2 + 9^2 = 82),然后下一个数字是 68 (因为 8^2 + 2^2 = 68),然后再下一个数字是 100 (因为 6^2 + 8^2 = 100),我们不断重复这个过程,最后我们得到了 1,因此 19 是一个快乐数,我们的函数应该返回 true。

对于正整数 19,通过反复计算平方和得到的下一个数字,其平方和构成的链表如下所示:

我们再来举一个例子:
从 116 开始,计算它的每个位置上的数字的平方和,得到的下一个数字是 38,然后再对 38 计算其每个位置上的数字的平方和,得到的下一个数字是 73,不断重复这个过程,我们最终得到了 58,然后从58开始,再继续往下计算平方和,我们又回到了 58 。由于我们回到了一个已经计算过的数字,由此可以知道这是一个循环,最后的计算结果是不可能得到 1 的,因此 116 不是快乐数,我们的函数要返回 false。

经过上面两个例子的分析,我们可以将求快乐数这道题转换成判断一个链表是否有环。如果遍历到某个节点为 1 时,说明链表有环,那么这个正整数是快乐数,如果遍历到重复的节点值,说明链表有环,那么这个正整数就不是快乐数。

判断链表是否有环,通常有两种方法,一种是使用哈希表来检测,另一种是使用快慢指针法。(详细步骤参阅:环形链表)。

代码实现

快慢指针法

  1. const isHappy = function(n) {
  2. let slow = n;
  3. let fast = getNext(n);
  4. while (slow != fast && fast != 1) {
  5. slow = getNext(slow);
  6. fast = getNext(getNext(fast));
  7. }
  8. return fast === 1;
  9. }
  10. const getNext = function(n) {
  11. let temp = 0;
  12. while (n) {
  13. temp += (n % 10) * (n % 10);
  14. n = Math.floor(n / 10);
  15. }
  16. return temp;
  17. }