[数学规律] 202. 快乐数(寻找数学规律、判断是否无限循环:Set、快慢指针法)

202. 快乐数

题目链接:https://leetcode-cn.com/problems/happy-number/

关键点(分类)

  • 数学规律、问题转化(找快乐数 → 判断是否无限循环)
  • 判断是否无限循环:HashSet、快慢指针法;

在这里插入图片描述

题目分析:(难度不止简单)

本题中,可以分为两部分:计算n的每一位的平方和;判断是否是快乐数。

关键问题在于,如何判断一个数不是快乐数,这里把问题转化为:

判断一个数是否会陷入计算平方和的无限循环,如果会陷入无限循环,就说明该数不是快乐数,

所以,问题转化为判断计算平方和会不会出现无限循环。
  • 思路1采用的方法是使用set存放每个计算过的平方和,一旦新得到的平方和存在于set中,说明出现循环。(关键在于空间不会无穷大的证明)
  • 思路2采用的方法是快慢指针法,模仿链表判断链表是否有环的做法,空间复杂度为O(1)

所以,本题的难点在于:

  1. set暴力解法证明空间不会溢出;
  2. 模仿链表找环使用快慢指针判断是否出现循环(很难想到);

思路1:set 暴力解法

不断取n的每一位计算平方和,然后拿平方和的每一位继续计算平方和,直到n==1,说明这个数是快乐数。

如何判断一个数是否陷入无限循环?

一个数如果计算平方和时陷入无限循环,说明该数不是快乐数,那么要如何判断n的计算过程是否陷入了无限循环?

如果n == 之前得到过的平方和,说明陷入了无限循环,n不是快乐数。

所以需要一个HashSet,存放每一个计算得到的平方和,当n计算的平方和不是1且存在于set中,则返回false。

但是,有没有可能n在计算平方和时陷入无限循环,且每个平方和都未曾出现过,导致占用的存储空间无限大

结论:当n为int型时,并不会出现每个平方和都未出现过而导致存储空间无限大。
证明:(参考:https://leetcode-cn.com/problems/happy-number/solution/kuai-le-shu-by-leetcode-solution/

n有1位,则n最大是9, 每一位的最大平方和为9;
n有2位,则n最大是99,每一位的最大平方和是9^2+9^2=162
n有3位,则n最大是999,每一位的最大平方和是9^2+9^2+9^2=243
n有4位,则n最大是9999,每一位的最大平方和是9^2+9^2+9^2+9^2=324
...
n有13位,则n最大是9999999999999,每一位的最大平方和是1053

可以发现,n的位数达到int型能取的最大位数时,就算每一位都取最大值9,得到的平方和也只有1053,说明就算出现无限循环,也只会在1053以内的数字出现,同时n的下一个数的取值也被大大缩小,变为n有4位的情况,就算n每一位都取最大值9,得到的平方和也只有243,说明就算出现无限循环,也只会在243以内的数字里出现。

所以set并不会出现占用无穷大空间的情况。

实现代码
class Solution {
    public boolean isHappy(int n) {
        Set<Integer> set = new HashSet<>();//存放每个出现过的n
        
        while(n != 1){
            //如果n曾经出现过,说明会出现循环,返回false
            if(set.contains(n)) return false;
            //如果n未曾出现过,则加入set
            set.add(n);
            //把n更新为每一位的平方和
            n = caculate(n);    
        }
        return true;
    }
    //计算n的每一位的平方和
    public int caculate(int n){
        int res = 0;
        while(n != 0){
            int bit = n % 10;//取当前最低位
            res += bit * bit;
            n = n / 10;
        }
        return res;
    }
}

思路2:快慢指针法(模仿链表找环,更巧妙)

判断一个数在计算平方和时是否会陷入无限循环,就是判断该平方和是否曾经出现过。在链表中等价为判断一个链表中是否有环,因为有环在遍历过程中就会遇到曾经访问过的节点。
链表中判断是否有环,使用的是快慢指针法,设置一个快指针一次走两步,慢指针一次走一步,如果有环,则这两个指针一定会相遇。

如何把链表的快慢指针法应用到这题上?

快指针一次走两步,相当于一次计算两次平方和:先计算一个平方和,在该平方和的基础上继续计算平方和。

慢指针一次走一步,相当于一次计算一次平方和。

当两个过程得到相同的计算结果时,说明遇到曾经出现的数,出现循环,返回false;

如果其中一个过程得到1,则返回true.

实现代码
class Solution {
    public boolean isHappy(int n) {
        int fast = n, slow = n;

        while(fast != 1 && slow != 1){
            //快指针计算两次平方和
            fast = caculate(fast);
            fast = caculate(fast);
            //慢指针计算一次平方和
            slow = caculate(slow);

            if(fast == slow && fast != 1) return false;
        }
        return true;
    }
    //计算n的每一位的平方和
    public int caculate(int n){
        int res = 0;
        while(n != 0){
            int bit = n % 10;//取当前最低位
            res += bit * bit;
            n = n / 10;
        }
        return res;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值