1.题目描述
编写一个算法来判断一个数 n 是不是快乐数。
快乐数的定义为:
- 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
- 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
- 如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n 是 快乐数 就返回 true ;不是,则返回 false
示例1:
输入:n = 19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1
示例2:
输入:n = 2
输出:false
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/happy-number
2.题目分析
2.1 哈希集合解法
首先对于求平方和来说,我们知道这个功能的实现就是一次次的取模运算和除法运算更新值。这个是很容易实现的。题目要求最终计算结果为1的就是快乐数字,但是最终二字的界定似乎有些麻烦,我们怎么知道最后是求完了呢?
在一开始我以为是求到平方和为个位数就结束了,但是这个想法是错误的,个位数还是可以继续求下去的,比如结果为4,我们就继续会有如下结果4-->16-->37-->58...
。这说明如果不是快乐数的话确实是一个无限的过程,但是我们再读题就会发现无限循环二字。既然是无限循环,那么必然是有规律可循的,也就是说求和的过程中计算出的平方和
会重复出现,这不就有思路了,如果不是快乐数,那么在其无限求解未停止之前过程中必然会有相同的值,反之如果在后续的第n次
求解中求出来的sumn
与前面第k
次求出的sumk
相同,那这个数必然不是一个快乐数字。
所以我们就可以得出以下求解步骤,我们用一个可以自动增长、可以快速查找的哈希集合保存已经求出的sum
,然后在每次新求出平方和sum‘
后就进行判断,这次判断有下面三种情况:
sum' == 1
:这个数是个快乐数sum' != 1且在前面出现过
:平方和计算陷入循环,不是快乐数,返回falsesum' != 1且未前面出现过
:继续计算
另外在解题中我发现如果直接使用递归会出现内存溢出的情况。
2.2 双指针解法
还是利用在计算求解过程中如果是非快乐数就会出现无限循环的计算结果,于是分别设计快慢指针fastPtr
和slowPtr
,slowPtr
每次移动一个位置,fastPtr
每次移动两个位置(参考前面求链表是否有环时的思路)。求解过程如下:
- 初始化
slowPtr
为当前数字n,fastPtr为下一个计算平方和后的值getNum(n);
- 如果
fastPtr
指向的值为1,则求解完成,这是个快乐数; - 如果
fastPtr
不为1,判断当前slowPtr
和fastPtr
指向的值是否相等,若相等则退出循环,这不是一个快乐数。否则继续向前移动当前的slowPtr
和fastPtr
一个和两个位置。
其中初始化时不必担心slowPtr就是1的情况,因为getSum(1)的结果还是1。
与上同理,快慢指针移动过程中也不必担心因为快指针移动两步而略过1。
3.题目解答
3.1 使用哈希集合存储平方和
class Solution {
public:
int getSum(int n){
int sum = 0;
//计算平方和
while(n){
sum += (n%10)*(n%10);
n=n/10;
}
return sum;
}
bool isHappy(int n) {
unordered_set<int> iSet;
while(true){
//平方和为1则是快乐数
int sum = getSum(n);
if(sum==1) return true;
//不是1就继续判断
if(iSet.find(sum) != iSet.end()) return false;
else{
iSet.insert(sum);
}
n=sum;
}
}
};
递归溢出的情况:
bool isHappy(int n) {
unordered_set<int> iSet;
int sum = getSum(n);
//计算平方和
//平方和为1则是快乐数
if(sum==1) return true;
//不是1就继续判断
else{
if(iSet.find(sum) != iSet.end()){
return false;
}else{
iSet.insert(sum);
return isHappy(sum);
}
}
}
3.2 快慢指针
class Solution {
public:
int getSum(int n){
int sum = 0;
//计算平方和
while(n){
sum += (n%10)*(n%10);
n=n/10;
}
return sum;
}
bool isHappy(int n) {
int slowPtr = n;
int fastPtr = getSum(n);
while(fastPtr != 1 && slowPtr != fastPtr){
slowPtr = getSum(slowPtr);
fastPtr = getSum(getSum(fastPtr));
}
return fastPtr == 1;
}
};