202. 快乐数

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是 无限循环,始终变不到 1。如果 可以变为 1,那么这个数就是快乐数。

如果 n 是快乐数就返回 True ;不是,则返回 False 。

示例:

输入:19
输出:true
解释:
1^ 2 + 9^2 = 82
8^2 + 2^2 = 68
6^2 + 8^2 = 100
1^2 + 0^2 + 0^2= 1

根据我们的探索,我们猜测会有以下三种可能。

最终会得到 1。
最终会进入循环。
值会越来越大,最后接近无穷大。

对于 3位数的数字,它不可能大于243(999,81*3=243)。这意味着它要么被困在 243 以下的循环内,要么跌到 1。4 位或4 位以上的数字在每一步都会丢失一位,直到降到 3 位为止。所以我们知道,最坏的情况下,算法可能会在243 以下的所有数字上循环,然后回到它已经到过的一个循环或者回到 1。但它不会无限期地进行下去,所以我们排除第三种选择。

即使在代码中你不需要处理第三种情况,你仍然需要理解为什么它永远不会发生,这样你就可以证明为什么你不处理它。

方法一:用哈希表检测循环

  1. 给一个数字 n,它的下一个数字是什么?
  2. 按照一系列的数字来判断我们是否进入了一个循环。用一个哈希表来记录是否已经计算过,判断是否循环。我们使用 HashSet 而不是向量、列表或数组的原因是因为我们反复检查其中是否存在某数字。检查数字是否在哈希集中需要O(1) 的时间,而对于其他数据结构,则需要O(n) 的时间。选择正确的数据结构是解决这些问题的关键部分。
var isHappy = function(n) {  
    var happy = function(n){
        let res = 0;
        while(n){
            let i = n%10;
            res += i*i;
            n = n/10 | 0;
        }
        return res;
    };
    
    let ans = happy(n);
    let map = new Map();
    while(ans !== 1){
        if(map[ans] == true) return false;
        else {
            map[ans] = true;
            ans = happy(ans);
        }
    }
    return true;
};

复杂度分析:
时间复杂度O(logn)
空间复杂度:O(logn)

方法二:快慢指针
我们不是只跟踪链表中的一个值,而是跟踪两个值,称为快跑者和慢跑者。在算法的每一步中,慢速在链表中前进 1 个节点,快跑者前进 2 个节点(对 happy(n) 函数的嵌套调用)。
如果 n 是一个快乐数,即没有循环,那么快跑者最终会比慢跑者先到达数字 1。
如果 n 不是一个快乐的数字,那么最终快跑者和慢跑者将在同一个数字上相遇。

/**
 * @param {number} n
 * @return {boolean}
 */
var isHappy = function(n) {  
    var happy = function(n){
        let res = 0;
        while(n){
            let i = n%10;
            res += i*i;
            n = n/10 | 0;
        }
        return res;
    };
    
    let slow = n;
    let fast = happy(n); 
    while(fast !== 1 && slow !== fast){
        slow = happy(slow);
        fast = happy(happy(fast));
    }
    return fast == 1;
};

复杂度分析

  • 时间复杂度:O(logn)。该分析建立在对前一种方法的分析的基础上,但是这次我们需要跟踪两个指针而不是一个指针来分析,以及在它们相遇前需要绕着这个循环走多少次。如果没有循环,那么快跑者将先到达 1,慢跑者将到达链表中的一半。我们知道最坏的情况下,成本是 O(2⋅logn)=O(logn)。 一旦两个指针都在循环中,在每个循环中,快跑者将离慢跑者更近一步。一旦快跑者落后慢跑者一步,他们就会在下一步相遇。假设循环中有 k个数字。如果他们的起点是相隔 k-1 的位置(这是他们可以开始的最远的距离),那么快跑者需要k−1步才能到达慢跑者,这对于我们的目的来说也是不变的。因此,主操作仍然在计算起始 n 的下一个值,即O(logn)。
  • 空间复杂度:O(1),对于这种方法,我们不需要哈希集来检测循环。指针需要常数的额外空间。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值