「这是我参与2022首次更文挑战的第27天,活动详情查看:2022首次更文挑战」。
题目描述🌍
编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」 定义为:
- 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
- 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
- 如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n 是 快乐数 就返回 true ;不是,则返回 false 。
示例 1:
输入:n = 19
输出:true
解释
示例 2:
输入:n = 2
输出:false
提示:
- <=
n<=
哈希法🚀
解题思路
⭐在开始做题之前,先分析下最终可能出现的几种情况:
- 最终结果为 1
- 最终陷入无限循环且到达不了 1
- 最终数值趋于无穷大
① 结果为 1:示例 1 给出的就是这种情况。
② 无限循环且不为 1
③ 趋于无穷大
题目限制条件中提到:
nextOne 是当前 n 的下一个数字
- n = 9,nextOne = 81
- n = 99,nextOne = 162
- n = 999,nextOne = 243
- n = 9999,nextOne = 324
- ...
- n = 9999999999,nextOne = 810
⭐对于任意一个 n 来说,它的位数就决定了其 nextOne 存在上限;而从 这种情况开始,其 nextOne 的位数一定是 n 的位数,所以无论 n 多大,经过不断的下一个数字,其值一定会陷入不大于 243 的循环之中(该分析对于第三种解题方法至关重要)!
比如对于 3 位数的数字而言,其循环过程中的天花板数字只能是 ,所以这种趋于无穷大的情况不可能出现,也就排除该情况了。
进入正题:既然结果只能是 1 或者无限循环,那我们可以用哈希表存储每一个出现过的数字,哈希表一旦重复则表明进入了循环。
代码
Java
class Solution {
// 哈希法
public boolean isHappy(int n) {
Set<Integer> hashSet = new HashSet<>();
while (!hashSet.contains(n)) {
hashSet.add(n);
n = nextOne(n);
if (n == 1)
return true;
}
return false;
}
// 获取下一个数
private int nextOne(int n) {
int sum = 0;
while (n != 0) {
int x = n % 10;
sum += x * x;
n /= 10;
}
return sum;
}
}
C++
class Solution {
public:
bool isHappy(int n) {
unordered_set<int> numbers;
while (numbers.find(n) == numbers.end()) {
numbers.insert(n);
n = nextOne(n);
if (n == 1)
return true;
}
return false;
}
int nextOne(int num) {
int sum = 0;
while (num != 0) {
int x = num % 10;
sum += x * x;
num /= 10;
}
return sum;
}
};
时间复杂度:
空间复杂度:,与时间复杂度相关
快慢指针🥇
解题思路
我们还可以通过快慢指针来标记元素,一旦进入了循环,快指针迟早能多绕一圈然后找到慢指针,所以快慢指针一旦相同且不为 1,那么则表示陷入无限循环了。
如果不会进入循环,快指针也一定会比慢指针先找到 1;虽然快指针是前进 2 次,但不用担心会错过 1,因为 。
代码
Java
class Solution {
// 快慢指针
public boolean isHappy(int n) {
int slow = n;
int fast = n;
do {
// slow 前进2次
slow = nextOne(slow);
// fast 前进1次
fast = nextOne(fast);
fast = nextOne(fast);
// 1^2 = 1; 所以无需担心错过1
if (fast == 1)
return true;
} while (slow != fast);
// slow == fast && fast != 1
return false;
}
// 获取下一个数
private int nextOne(int n) {
int sum = 0;
while (n != 0) {
int x = n % 10;
sum += x * x;
n /= 10;
}
return sum;
}
}
C++
class Solution {
public:
bool isHappy(int n) {
int slow = n;
int fast = n;
do {
slow = nextOne(slow);
fast = nextOne(fast);
fast = nextOne(fast);
if (fast == 1)
return true;
} while (slow != fast);
return false;
}
int nextOne(int num) {
int sum = 0;
while (num != 0) {
int x = num % 10;
sum += x * x;
num /= 10;
}
return sum;
}
};
时间复杂度:
空间复杂度:
数学法🛕
解题思路
在分析为何最终值不会趋于无穷大的那部分,我们得出了一个结论:如果存在循环,那么一定会陷入不大于 243 的循环之中。
通过仔细查找就可以锁定唯一的循环了:,所以一旦出现循环,无论如何都逃脱不了这个唯一的循环圈。
代码
Java
class Solution {
// 数学方法
public boolean isHappy(int n) {
// 保存常数个数字
Set<Integer> hashSet = new HashSet<>(Arrays.asList(4, 16, 37, 58, 89, 145, 42, 20));
while (n != 1 && !hashSet.contains(n)) {
n = nextOne(n);
}
return n == 1;
}
private int nextOne(int n) {
int sum = 0;
while (n != 0) {
int x = n % 10;
sum += x * x;
n /= 10;
}
return sum;
}
}
C++
class Solution {
public:
bool isHappy(int n) {
unordered_set<int> circulation{4, 16, 37, 58, 89, 145, 42, 20};
while (n != 1 && circulation.find(n) == circulation.end()) {
n = nextOne(n);
}
return n == 1;
}
int nextOne(int num) {
int sum = 0;
while (num != 0) {
int x = num % 10;
sum += x * x;
num /= 10;
}
return sum;
}
};
时间复杂度:
空间复杂度:,因为仅保存常数个值到哈希表中。
最后🌅
该篇文章为 「LeetCode」 系列的 No.25 篇,在这个系列文章中:
- 尽量给出多种解题思路
- 提供题解的多语言代码实现
- 记录该题涉及的知识点
👨💻争取做到 LeetCode 每日 1 题,所有的题解/代码均收录于 「LeetCode」 仓库,欢迎随时加入我们的刷题小分队!