1.题目要求

2.题目链接
202. 快乐数 - 力扣(LeetCode)
3.题目分析
首先 ,因为需要频繁地用到数字变为各个位上的平方的过程,我们可以将"对于一个正整数,每一次将该数替换为它每个位置的数字的平方和 "这一操作抽象出来,**定义成一个方法funtion(int n)**以供调用。
第二 ,我们可以从题目中得知,数字经过funtion操作后 ,只有两种结果,一种是结果是1 ,一种是无限循环但是不等于1 (不存在第三种情况,也就是数字无限演化但既不等于1也不循环,可以通过鸽巢定理简单证明)。又因为funtion(1)=1,所以说结果为1后也可以认为陷入了循环。
所以数字经过funtion操作后,最终都会陷入循环,那么我们就可以得到:

第三 ,这种情况就是我们非常熟悉的链表带环问题了。
链表带环问题就是需要我们判断一个链表是否带有环,我们通过定义快慢双指针,快指针fast一次走两步,慢指针slow一次走一步,进行循环,如果链表带环,那么双指针跑不出去链表,也就是会无限循环,直到双方相遇;如果链表不带环,那么快指针fast就会触发fast==null||fast.next==null这一条件终止while循环。
这里我们是将快乐数的演化操作想象成一条带环的链表,来求快慢指针相遇的节点的值,因为快慢指针的相遇一定是在环内,所以我们可以得到循环的值,如果是1,则证明该数是快乐数。如果不是,则证明不是。
java
public boolean isHappy(int n) {
int slow=n;
int fast=function(n);
while(fast!=slow){
slow=function(slow);
fast=function(function(fast));
}
if(slow==1){
return true;
}
else{
return false;
}
}
这里因为我们已知链表有环,所以我们可以直接设置while结束条件为fast!=slow。但是fast和slow都是从头结点n开始的,根本无法进入while循环,所以我们可以将fast指针往前移动一步(int fast=function(n);) 。
4代码细节
(1)为什么fast在这里可以先走一步?
正确性证明
快慢指针法的核心是:只要存在环,快指针一定会追上慢指针,无论两者的初始位置如何。具体分析:
- 环的存在性 :若
n
不是快乐数,则序列必然陷入某个循环(环)。 - 相对速度 :
fast
每次比slow
多走一步(slow
走 1 步,fast
走 2 步),因此两者的相对距离每次减少 1。 - 相遇必然性 :无论
fast
和slow
的初始位置相差多少步,只要存在环,fast
最终一定会追上slow
(相对距离减至 0)。
提前一步的影响
- 加速相遇 :
fast
提前一步,相当于提前进入环,可能减少相遇所需的迭代次数。 - 不改变结果:相遇点的位置可能不同,但最终仍能判断是否存在环(是否等于 1)。
(2)快慢指针移动逻辑
原问题中,fast=fast.next.next,slow=slow.next。这里我们也要对应的写为:
java
while(fast!=slow){
slow=function(slow);
fast=function(function(fast));
}
如果不小心写为这样:
java
while(fast!=slow){
slow=function(n);
fast=function(function(n));
}
就会导致slwo和fast永远都是固定值,双指针永远不移动。