202 快乐数
编写一个算法来判断一个数 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
提示:
1 <= n <= 231 - 1
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/happy-number
解决方案:
提供思路
1)用哈希集合检测循环
用7做测试,可以得到1。
用116做测试,可以在58处循环。
我们猜测,可能出现如下3种情况:
1.最终会得到 1。
2.最终会进入循环。
3.值会越来越大,最后接近无穷大。
第三种情况的处理,如下表
Digits | Largest | Next |
---|---|---|
1 | 9 | 81 |
2 | 99 | 162 |
3 | 999 | 243 |
4 | 9999 | 324 |
13 | 9999999999999 | 1053 |
对于 3 位数的数字,它不可能大于 243。这意味着它要么被困在 243 以下的循环内,要么跌到 1。4 位或 4 位以上的数字在每一步都会丢失一位,直到降到 3 位为止。所以我们知道,最坏的情况下,算法可能会在 243 以下的所有数字上循环,然后回到它已经到过的一个循环或者回到 1。但它不会无限期地进行下去,所以我们排除第三种选择。
即使在代码中你不需要处理第三种情况,你仍然需要理解为什么它永远不会发生,这样你就可以证明为什么你不处理它。
2)快慢指针法
通过反复调用 getNext(n) 得到的链是一个隐式的链表。隐式意味着我们没有实际的链表节点和指针,但数据仍然形成链表结构。起始数字是链表的头 “节点”,链中的所有其他数字都是节点。next 指针是通过调用 getNext(n) 函数获得。
意识到我们实际有个链表,那么这个问题就可以转换为检测一个链表是否有环。因此我们在这里可以使用弗洛伊德循环查找算法。这个算法是两个奔跑选手,一个跑的快,一个跑得慢。在龟兔赛跑的寓言中,跑的慢的称为 “乌龟”,跑得快的称为 “兔子”。
不管乌龟和兔子在循环中从哪里开始,它们最终都会相遇。这是因为兔子每走一步就向乌龟靠近一个节点(在它们的移动方向上)。
3)数学
前两种方法是你在面试中应该想到的。第三种方法不是你在面试中会写的,而是针对对数学好奇的人,因为它很有趣。
下一个值可能比自己大的最大数字是什么?根据我们之前的分析,我们知道它必须低于 243。因此,我们知道任何循环都必须包含小于 243 的数字,用这么小的数字,编写一个能找到所有周期的强力程序并不困难。
如果这样做,您会发现只有一个循环:4→16→37→58→89→145→42→20→4。所有其他数字都在进入这个循环的链上,或者在进入 1 的链上。
因此,我们可以硬编码一个包含这些数字的散列集,如果我们达到其中一个数字,那么我们就知道在循环中。
上代码:
//1
class Solution
{
private int getNext(int n)
{
int totalSum = 0;
while (n > 0)
{
int d = n % 10;
n = n / 10;
totalSum += d * d;
}
return totalSum;
}
public bool IsHappy(int n)
{
HashSet<int> seen = new HashSet<int>();
while (n != 1 && !seen.Contains(n))
{
seen.Add(n);
n = getNext(n);
}
return n == 1;
}
}
//2
class Solution
{
public int getNext(int n)
{
int totalSum = 0;
while (n > 0)
{
int d = n % 10;
n = n / 10;
totalSum += d * d;
}
return totalSum;
}
public bool IsHappy(int n)
{
int slowRunner = n;
int fastRunner = getNext(n);
while (fastRunner != 1 && slowRunner != fastRunner)
{
slowRunner = getNext(slowRunner);
fastRunner = getNext(getNext(fastRunner));
}
return fastRunner == 1;
}
}
//3
class Solution
{
public int getNext(int n)
{
int totalSum = 0;
while (n > 0)
{
int d = n % 10;
n = n / 10;
totalSum += d * d;
}
return totalSum;
}
public bool IsHappy(int n)
{
HashSet<int> cycleMembers =
new HashSet<int>(new int[8] { 4, 16, 37, 58, 89, 145, 42, 20 });
while (n != 1 && !cycleMembers.Contains(n))
{
n = getNext(n);
}
return n == 1;
}
}
个人感悟:讲真我开始没有考虑第三种情况,逻辑不麻烦,但是可能会直接按第一种走;适当的考虑下第2,3种写法也蛮不错。但个人还是感觉,2,3有点取巧。
以上是碰到的第二百零二题,后续持续更新。感觉对你有帮助的小伙伴可以帮忙点个赞噢!