题目描述
编写一个算法来判断一个数 n
是不是快乐数。
「快乐数」 定义为:
- 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
- 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
- 如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n
是 快乐数 就返回 true
;不是,则返回 false
。
示例 1:
输入:n = 19
输出:true
解释:
1^2 + 9^2 = 82
8^2 + 2^2 = 68
6^2 + 8^2 = 100
1^2 + 0^2 + 0^2 = 1
示例 2:
输入:n = 2
输出:false
提示:
1 <= n <= 231 - 1
题解
方法一:哈希表
时空复杂度: O(243⋅3+logn+loglogn+logloglogn)… = O(logn) O(logn)
这里的时空复杂度分析有些复杂,这里贴出官方的分析,大家可以参考[202.快乐数](202. 快乐数 - 力扣(LeetCode))
这题根据题意可以直接进行模拟,然后将出现过的数存入Set集合中,在模拟过程中如果出现了在Set集合中存在的数字,则可以判断不是快乐数,当n等于1的时候就代表为快乐数字,这题时间复杂度笔者不会分析(泪目)。
代码:
public boolean hashMethod(int n) {
//创建集合存储出现过的元素
Set<Integer> set = new HashSet();
//当n不等于1的时候
while(n != 1) {
//加入当前数字进入集合中
set.add(n);
//创建变量统计总和
int sum = 0;
while(n != 0) {
sum += Math.pow((n % 10),2);
n /= 10;
}
//判断当前数字是否在集合中出现过
if(set.contains(sum)) {
return false;
}
//改变n
n = sum;
}
//返回true
return true;
}
方法二:快慢指针
时空复杂度: O(logn) O(1)
这里我们可以参考判圈的算法,虽然我们这里是对数字的处理,但是我们也可以类比到链表结构的前后节点,如果我们使用了判圈算法,可以省去存储遍历过的节点的Set集合的空间。具体思想就是创建一个快指针,一个慢指针,快指针一次移动两个单位,慢指针一次移动一个单位,这样子如果当有环出现的时候,快指针在环内就能追上慢指针了。
代码:
public boolean isHappy(int n) {
//定义快慢指针
int slow = n;
int fast = getNext(n);
//当快指针不为1并且快慢指针不相等的时候继续循环
while(fast != 1 && slow != fast){
slow = getNext(slow);
fast = getNext(getNext(fast));
}
//判断快指针是否等于1,即是否为快乐数,或者两个指针在环中相遇了
return fast == 1;
}
public int getNext(int n) {
//定义求和变量
int sum = 0;
while(n != 0) {
sum += Math.pow((n % 10),2);
n /= 10;
}
return sum;
}
总结
这道题目考察了我们,模拟,选用合适的数据结构存储数据的能力,这里我们合适使用Set集合存储,因为,Set中查找元素只需要O(1)的时间复杂度,而链式结构需要O(n)。方法二的快慢指针适用于查找存在环形结构的题目。好了,本题的就说到这里!