1.题目
题目中一定要理解快乐数的含义,否则题目难度直逼困难。
在示例1中n=19,经过几步操作后结果变成1。
那么示例2中n=2是什么情况呢:
2->4->16->37->58->89->145->42->20->4(与前面的4形成闭环)
在计算机中int所能存储的数据范围内,只有这两种情况,没有第三种情况,稍后我会进行简单证明,这个证明过程并不重要,能看懂就行。
2.算法思路
那么如何把示例1和示例2联系在一起呢:
其实示例1最终的结果也是一个闭环,只不过这个环中只有一个数字1。
而示例二中的闭环无非就是不止一个数,且不可能有1。
我们就可以通过闭环中有没有1来判断这个数是否是快乐数。
回到算法本身,我们的算法是双指针,但是这道题目只是单纯的数字,难道真的需要定义一个实实在在的指针嘛?其实不是,双指针只是一个思想,像我的前两篇文章中的数组下标可以代表指针,在这篇文章中一个数字也可以代表指针!思想相同,就属于同一类算法。
那么这道题目就可以用快慢指针来解决,定义一个慢指针slow,和一个快指针fast。慢指针进行一次操作,快指针进行两次操作,因为最后的结果是一个闭环,所以快慢指针一定会相遇,相遇时判断它们是否等于1。等于1就是快乐数,否则就不是。
3.提交结果与代码实现
class Solution {
public:int GetSum(int x){int sum=0;while(x){int a=x%10;x/=10;sum+=a*a;}return sum;}bool isHappy(int n) {int slow=n,fast=n;do{slow=GetSum(slow);//慢指针走一步fast=GetSum(GetSum(fast));//快指针走两步}while(slow!=fast);return slow==1;}
};
4.证明一定存在闭环
在计算机中int所能存储的最大数字是2^31。这是一个十位数,拿最大的十位数来说,经过一次题目中所述的操作,结果就是:10*9^2=810。
也就是说,从0到2^31之间的所有数字经过一次操作后所得的结果一定在落在【1,810】这个区间之内,所以在0到2^31之间任意一个数经过811次操作后,每一次操作的结果一定都落在【1,810】区间内,而这个区间一共有810个整数,811次操作会得到811个整数,就一定可以推出至少有两次操作的结果是相等的!
这个原理就是鸽巢原理,意思就是有n个巢,n+1个鸽子,那么一定只少有一个巢中有两只鸽子!