采用快慢指针办法破循环
来源于leetcode的一道简单难度的算法题“快乐数”
算法要求:给定一个任意数字,数字的每一位都可拆解为多个一位数(19 -> 1,9 234 -> 2,3,4),所有的一位数做平方和之后产生新的数字(19 -> 1,9. sum = 1*1 + 9*9 ,sum为新的值)之后,重复上述拆解及平方和操作,直到最后得到的结果为1,就是快乐数
这里我本来的想法是通过递归去不断的拆解循环,终止条件为直到找到为1的结果。但会产生一个问题,并不是所有数都能最终得到1的值,有的数会卡在一个循环里永远也得不到1的值,会在某个数形成环,最终无限循环下去导致stackOverFlow。
那么为了解决这个问题呢,就必须去判断到底是在哪个数形成了没有出口的环
为什么要判断这个呢,因为哪怕是快乐数最终的值“1”,如果他没有终止条件(判断为1)还是会不断循环下去,1的平方还是1,也就是说其实跟其他的值循环没有区别的,主要是1的这个值可以被终止,但其他的随机值没有办法终止循环,所以这个时候就要找到其他循环的数去判断是否是“1”,如果是“1”则是快乐数,不是的话就可以返回false了(都陷入循环了,不返回也不会有期待值)
这里判断哪个数形成了环的思路,可以考虑是哪个数第一次出现了重复的值(意味着绕了一圈又回到了起点)
初步可以考虑通过Set去实现,将每一个结果值都放入Set中,直到有一次放入结果值的时候并没有成功放进去(Set不接受重复的数),就可以判断出是哪个数形成了环
比较好的处理办法则是通过快慢指针的办法
快慢指针:两个不同的指针,同一时间内,快的一次走两步,慢的走一步。当遇到循环时,两个指针都在循环中走,而最后的结果是:快指针必然会追上慢指针,他们重合(追上)的地方的值,就是开始出现循环的值。可以通过这个办法很轻松的找出循环的入口。
贴一段实现的Java代码:
class Solution {
public boolean isHappy(int n) {
int fast = n; int slow = n;
do {
//慢指针一次走一步
slow = getSum(slow);
//快指针一次走两步
fast = getSum(fast);
fast = getSum(fast);
}while (fast != slow);
//快慢指针相遇判断值
if(fast == 1){
return true;
}
return false;
}
//处理每一个值
public int getSum(int n){
int sum = 0;
int a = 0;
while (n > 0){
a = n%10;
sum += (a*a);
n = n/10;
}
return sum;
}
}