目录
1. 题目描述
2. 解题思路
这道题目的逻辑是很简单的,难点是在于如何判断已经循环够了,该跳出循环了?首先,如果这个数是快乐数,最终会等于1,那么就可以直接跳出循环并返回了;但是,如果这个数不是快乐数,他就会无限循环,那还有什么规律吗?我发现,对于10以下的数,只有1和7是快乐数,其他都不是,那么这就意味着当我迭代发现当前数是小于10并且不是7和1时,就该结束循环返回false
了,这种解法我称之为规律法,可能并不是十分正统的思路。
那么有其他正统的思路吗?如果我能记住我之前判断过什么数字,当我又重新遇到这个数字的时候,那不就代表着存在死循环,可以返回false
了。实现方法自然而然就想到了哈希表。
既然说遇到某个数字会产生死循环,那么是不是特定的几个数字呢?我手写推算了几位数字发现,对于非快乐数而言最终都会掉进4, 16, 37, 58, 89, 145, 42, 20这个循环当中,那就意味着只要在迭代过程中的数是以上之一,那么他就不是快乐数了,这是基于记忆(哈希表)和规律的思路。
循环,相当于一个链表中拥有回路,在【LeetCode - Java】141. 环形链表 (简单)中介绍过一种高效的快慢指针算法,可以快速地进行判断,对于这一道题目是同样适用的。
3. 代码实现
3.1 规律法
public boolean isHappy(int n) {
int sum;
while (n != 1) {
if (n < 10 && n != 7)
return false;
sum = 0;
while (n != 0) {
sum += (int) Math.pow(n % 10, 2);
n /= 10;
}
n = sum;
}
return true;
}
3.2 哈希表
public boolean isHappy(int n) {
HashSet<Integer> set = new HashSet<>();
int sum;
while (!set.contains(n)) {
set.add(n);
sum = 0;
while (n != 0) {
sum += Math.pow(n % 10, 2);
n /= 10;
}
if (sum == 1)
return true;
n = sum;
}
return false;
}
3.3 哈希表+规律
public boolean isHappy(int n) {
HashSet<Integer> set = new HashSet<>(Arrays.asList(4, 16, 37, 58, 89, 145, 42, 20));
while (!set.contains(n)) {
if (n == 1)
return true;
int sum = 0;
while (n != 0) {
sum += Math.pow(n % 10, 2);
n /= 10;
}
n = sum;
}
return false;
}
3.4 快慢指针
public boolean isHappy(int n) {
int slow = n;
int fast = next(next(n));
while (slow != fast && fast != 1) {
slow = next(slow);
fast = next(next(fast));
}
return fast == 1;
}
public int next(int n) {
int sum = 0;
while (n != 0) {
// sum += Math.pow(n % 10, 2);
sum += (n % 10) * (n % 10);
n /= 10;
}
return sum;
}
3.5 对比
理论上,上述所有方法的时间复杂度都是线性复杂度,因此时间差异都不大,哈希表的时间消耗较大应该是哈希表的存储查询导致的。另外,一个很有意思的点是在快慢指针算法当中,我尝试自己使用乘法进行平方运算,结果发现比使用Math.pow
函数内存消耗更低。