一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情
暴力
拿到题目之后,很容易想到用while循环去暴力搜索,但是根据快乐数的第二条规则: 也可能是 **无限循环** 始终变不到 1,如果是始终变不到1,那么以 n === 1为终止条件的while循环就会陷入死循环。所以暴力枚举是无法解决这个问题的。
抽象成已知问题求解
分析一下:
- 如果是快乐数的话,那么结果肯定会变成1;
- 如果不是快乐数,
n的值会怎么变化?会陷入循环?还是会一直变大,直至无穷大。
考察一下n的变化规率:
| 当前数字 | 下一个数字 | 下下个 |
|---|---|---|
| 5 | 25 | 29 |
| 99 | 162 | 41 |
| 999 | 243 | 29 |
| 9999 | 324 | 29 |
| 99999 | 405 | 41 |
| ... | ... | ... |
| 999999999 | 729 | 134 |
| 2^31 - 1 | 260 | 40 |
从上表可以看出,第二轮最大是729,那么第三次的计算最大值肯定不会超过(三位最大数)999计算之后的值243,那么就可以排除上面说的如果不是快乐数,n会一直变大的情况。换句话说,如果n不是快乐数,多次计算之后,n会陷入无限循环,如果把无循环中的重复出现的值当做一个环,那么这个题目就可以抽象成 判断链表是否是环形链表。那么就可以使用快慢指针或者哈希表来解决。
// 快慢指针
function isHappy(n: number): boolean {
if (n === 1) return true
let slow = n
let fast = getDigitSum(n)
if (fast === 1) {
return true
}
fast = getDigitSum(fast)
while(slow !== 1 && fast !== 1 && fast !== slow) {
slow = getDigitSum(slow)
fast = getDigitSum(fast)
if (fast === 1) {
return true
}
fast = getDigitSum(fast)
}
// 如果快慢指针相遇,说明有在环中相遇,表明n陷入循环,不是快乐数
return fast !== slow
};
// 工具函数,计算每个位置平方的总和
function getDigitSum(n: number) {
let sum = 0
while(n !== 0) {
let num = n % 10
sum += Math.pow(num, 2)
n = (n / 10) | 0
}
return sum
}