快乐数
本节对应代码随想录中:代码随想录,讲解视频:暂无
习题
编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」 定义为:
- 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
- 然后重复这个过程直到这个数变为 1,也可能是无限循环但始终变不到 1。
- 如果这个过程结果为 1,那么这个数就是快乐数。
- 如果 n 是快乐数就返回 true ;不是,则返回 false 。
示例 1:
输入:n = 19 输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1
set 解法
刚开始看到题目后,一直纠结于题目中说的可能无限循环。拆分数字并判断和是否为1并不困难,但如果程序可能陷入无限循环,那一直找不到结果就会超时,但是和一直不为1,又无法确定后面的和是否会为1。
没有思路的时候可以输出下每个数的和,看看整个过程,代码如下
int n = 12;
for (int i = 0; i < 20; i++) {
int sum = 0;
while (n != 0) {
sum += (n % 10) * (n % 10);
n /= 10;
}
cout << sum << "#";
n = sum;
}
每次会输出数字的和,共输出20次
当 n=12时,会输 5#25#29#85#89#145#42#20#4#16#37#58#89#145#42#20#4#16#37#58#
,注意看数字出现了循环。#89#145#42#20#4#16#37#58#
从89开始就有了这一段循环。既然出现循环,说明当计算的和为89的时候,程序计算一段时间和后又会回到89。也就是说,如果一个数字出现了第二次,那么在这个数前面一定存在一段循环,那这个数字 n 一定不是快乐数,因此就可以用 unordered_set
来判断元素是否重复出现,若重复出现则可以返回 false,终止这个无限循环。
class Solution {
public:
bool isHappy(int n) {
unordered_set<int> mySet;
while (1) {
int sum = 0;
// 计算每个位置上的数字的平方和
while (n != 0) {
sum += (n % 10) * (n % 10);
n /= 10;
}
// 和为1则说明是快乐数
if (sum == 1) {
return true;
}
// 如果set中存在sum,说明一定会陷入循环中,即不可能是快乐数
if (mySet.count(sum)) {
return false;
}
mySet.insert(sum);
n = sum; // 将sum赋值给n,继续计算数字n每个位置上的数字的平方和
}
}
};
- 时间复杂度:O(logn)。查找给定数字的下一个值的成本为 O(logn)
- 空间复杂度:O(k)。其中 k 为循环的次数,每次循环都会存入 unordered_set 中。