【路飞 - 算法体能训练】快乐数

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战

每日3天分钟,快速学算法

算法体能训练计划 - 第一周

9d5cef69ly1fy8mpo6qvtg208w06oq8x.gif

题目地址 - 202. 快乐数

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

「快乐数」定义为:

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

19 -> 82 -> 68 -> 100 -> 1

1^2 + 9^2 = 82

8^2 + 2^2 = 68

6^2 + 8^2 = 100

1^2 + 0^2 + 0^2 = 1


思路

  • 题目可以转化为,判断一个链表是否有环。
  • 如果遍历某个节点为1,说明没环,就是快乐数。
  • 如果遍历到重复的节点值,说明有环,就不是快乐数。

思考

Q: 会不会数字越来越大?

位数 最大值 下一个数
1 9 81
2 99 162
3 999 243
4 9999 324

A: x -> x^2什么时候最大, 199999999...99981*9+1 = 730,所以最大也是730,不会越来越大

背景:

c++中整型的范围是: -2 147 483 648 〜+2 147 483 647,所以符合上面最大的数是1 999 999 999,它的每项平方和是 81 * 9 + 1 = 730,意思说平方和最大就是730了,最多有730个节点。

解答

set集合判断是否有环

一直查找下一个快乐数并记录到set, 如果发现重复的数则不是快乐数,反之快乐数成立。

  • 如果它不在Set集合中,就应该添加它
  • 如果在Set集合中,说明已经开始循环了,那么它就不是快乐数

图解

算法图解_202.gif

// 获取下一个快乐数
const getNext = n => {
  let total = 0
  while(n > 0) {
    total += Math.pow(n % 10, 2)
    n = ~~(n / 10)
  }

  return total
}


const isHappy = n => {
  const visited = new Set()
  while (n !== 1 && getNext(n) !== 1) {
    // 如果重复说明不是快乐数
    if (visited.has(n)) {
      return false
    }

    visited.add(n)
    n = getNext(n)
  }
  
  // 到达这里说明不重复
  return true
}
复制代码

检查元素是否在Set集合: O(1)

时间复杂度: O(logn)

快慢指针

快指针比慢指针每次多走一步,如果有环肯定会相遇,不相遇则是快乐数。

图解

算法图解_202_2.gif

const getNext = n => String(n).split('').reduce((pre, cur) => pre + Math.pow(cur, 2), 0)

const isHappy = n => {
  let fast = n
  let slow = n

  while(fast !== 1 && getNext(fast) !== 1) {
    fast = getNext(getNext(fast))
    slow = getNext(slow)
    // 到达1,则是快乐数
    if (fast === 1) {
      return true
    }

    if (fast === slow) {
      return false
    }
  }

  return true
}
复制代码

猜你喜欢

转载自juejin.im/post/7054206121459843080