题目链接
题目描述
编写一个算法来判断一个数 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
- 值越来越大,直到无穷大
- 进入死循环
其中,情况2是我们不想看到的,因为它很难处理,事实上,这种情况是不存在的,证明如下:
最大的 位数为
, 其快乐数为
;
最大的 位数为
, 其快乐数为
;
最大的 位数为
, 其快乐数为
;
令 得
即当一个数大于等于4位时,它的快乐数是不断变小的,直到变为三位数,且三位数的快乐数不大于243,由此就可以排除情况2。
因此,本题的关键是判断是否有环。
思路1:哈希表(哈西集合)
如果一个数不在哈西集合里,就添加它;如果在集合中,说明存在环。
思路2:快慢指针
思路3:数学
由之前的分析我们可知,对于输入的数来说,它的快乐数最终都会收敛到243以内,因此,若有环,也是在243内循环;
对于243这种较小的数字,我们可以编程寻找环。
最后得出只存在 。因此,我们可以判断输入的数最终的快乐数是否落在这个环里即可。
题解
思路1:哈希表(哈西集合)
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) {unordered_set<int> visited;while (n != 1 && visited.find(n) == visited.end()) {visited.insert(n);n = getSum(n);}return 1 == n;}};
思路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;
}
};
