知识点补充:
根据鸽巢原理,当一个数求其每一位的平方的和时,总会出现循环,即每一位平方的和均为其本身,这又两种情况,一是和为1,接下来怎么求都是1;二是和为其他数,但每一位的平方的和均为其本身。
1.常规解法
将每一个数的每一位的平方的和求出后放到一个顺序表中,每一次都判断该数时候存在于顺序表中,若存在,则判断是否为1,若为1,则是快乐数,返回true;若不是,则不是快乐数,返回false,代码如下:
public boolean isHappy(int n) {
ArrayList<Integer> list = new ArrayList<>();
int num = n;
while (true) {
num = getNum(num);
if (list.contains(num)) {
if (num == 1) {
return true;
} else {
return false;
}
}
list.add(num);
}
}
public int getNum(int n) {
int num = 0;
while (n != 0) {
num += (n % 10) * (n % 10);
n /= 10;
}
return num;
}
说明:getNum方法即求n的每一位的平方的和。
2.双指针算法
根据前面的知识补充,可知无论怎么求,最后都会是一下图:
学过链表的同学应该都对这张图不陌生,和判断链表是否有环一样,都有一个循环,这是我们就可以根据快慢指针来判断是否有环,若两指针相遇,即有环,反之则无环。
这题也是一样,我们可以定义两个“指针”slow和fast,slow一次走一步,相当于求一次每一位的平方的和,fast一次走两步,相当于求两次每一位的平方的和,根据上述补充知识,该循环一定有环,即fast与slow一定会相遇,只需要判断相遇的时候fast或slow的值是不是1即可,若为1,则为快乐数,返回true,若不为1,则不是快乐数,返回false,代码如下:
public boolean isHappy(int n) {
int slow = n;
int fast = getNum(getNum(n));
while (fast != slow) {
slow = getNum(slow);
fast = getNum(getNum(fast));
}
if (fast == 1) {
return true;
} else {
return false;
}
}
public int getNum(int n) {
int num = 0;
while (n != 0) {
num += (n % 10) * (n % 10);
n /= 10;
}
return num;
}
需要注意的是,一开始定义fast和slow时,不能让两个都为n,因为都为n后,循环就进不去了,可以让fast先走两步,这样就能进入循环了。