题目
编写一个算法来判断一个数是不是“快乐数”。一个“快乐数”定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是无限循环但始终变不到 1。如果可以变为 1,那么这个数就是快乐数。
示例
input: 19
output: true
解释:
1^2 + 9^2 = 82
8^2 + 2^2 = 68
6^2 + 8^2 = 100
1^2 + 0^2 + 0^2 = 1
解法
Set ( Python )
class Solution:
def isHappy(self, n: int) -> bool:
s = set()
res = n
while(res != 1):
res = self.calPowSum(res)
if(res in s):
return False
else:
s.add(res)
return True
def calPowSum(self, n: int) -> int:
sum = 0
while(n > 0):
sum += (n % 10) ** 2
n = n // 10
return sum
基本思路
想要做出这道题,就需要找出背后的规律——循环。如果没有循环,那么平方和每次就不相同,这就很难做了。那我们要怎么证明有循环呢?
如果平方和永不相同,那么就代表平方和的数值范围无穷大。所以要证明有循环,只需要证明平方和范围有限制即可。假设在计算了k (
k
>
=
0
k>=0
k>=0) 次平方和后结果为
x
k
x_k
xk,位数为D
。如果D == 1
,那么
x
k
+
1
x_{k+1}
xk+1最大为
9
2
9^2
92 = 81;如果D == 2
,那么
x
k
+
1
x_{k+1}
xk+1最大为
9
2
+
9
2
=
162
9^2 + 9^2 = 162
92+92=162。说这些是为了说明当
d
(
x
k
)
<
=
2
d(x_k) <= 2
d(xk)<=2 时( d(x) 表示x的位数 ),其平方和
x
k
+
1
x_{k+1}
xk+1可能会比起
x
k
x_k
xk更大,但最大也就是达到三位数。如果
x
k
+
1
x_{k+1}
xk+1为三位数,那么之后的
x
k
+
2
x_{k+2}
xk+2只会比它小,读者可以自己去尝试一下,当然后面多次迭代之后还可能出现比
x
k
+
1
x_{k+1}
xk+1更大的三位数,但是不管怎么样也不会超过
9
2
∗
3
<
=
243
9^2 *3 <= 243
92∗3<=243。如果一开始D > 3
,也就是初始至少为1000,其值也会慢慢缩小到三位数乃至243以内。这样就说明存在某个循环。
既然有循环,那么只要经过 k( k >= 0 ) 次计算平方和进入这个循环,就说明这个数不会是快乐数了。怎么判断这个数是不是进入了循环?只要某个数第二次出现,就可以判定了。所以使用Set
来存储之前出现过的数字,一旦重复数字出现,则不是快乐数。
复杂度分析
有人说最多计算六次平方和就可以通过所有测试用例,那么时间复杂度可以看作为
O
(
1
)
O(1)
O(1)。空间用到一个set
,复杂度为O(m)
,这里m
指的是整个循环的长度,但没有包括未进入循环之前的计算结果长度。
参考资料
Floyd Cycle Detection Algorithm ( C )
int digitSquareSum(int n) {
int sum = 0, tmp;
while (n) {
tmp = n % 10;
sum += tmp * tmp;
n /= 10;
}
return sum;
}
bool isHappy(int n) {
int slow, fast;
slow = fast = n;
do {
slow = digitSquareSum(slow);
fast = digitSquareSum(fast);
fast = digitSquareSum(fast);
} while(slow != fast);
if (slow == 1) return 1;
else return 0;
}
基本思路
依然是基于循环的算法,只是发现循环的方法不同。这里slow
和fast
相当于两个指针,在每轮迭代中,slow
只计算一次,fast
计算了两次,这样当slow
到达第二次循环的第一个值时,fast
已经到第三次循环的第一个值了。这时候slow
和fast
的值是相等的,就可以判断出循环了。但是如果一个无规律序列里,某个数恰好出现了多次,而且间隔相同,那么这个算法就失效了。
复杂度分析
时间复杂度为O(1)
, 空间复杂度也为O(1)
。
参考资料
除此之外,还有一些纯找数学规律的算法,这里就不介绍了。如有问题,欢迎指正!