LeetCode 202. Happy Number

题目

Write an algorithm to determine if a number is "happy".

A happy number is a number defined by the following process: Starting with any positive integer, replace the number by the sum of the squares of its digits, and repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1. Those numbers for which this process ends in 1 are happy numbers.

Example: 

Input: 19
Output: true
Explanation: 
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1

这道题的意思是,给定一个数字,让这个数字的每一位数平方求和,得到的新数再继续这么操作,最后看是否会得到1,如果是的话就是happy number,否则就不是。

这道题看到的时候还是很懵逼的,不知道怎么去让它判断,看了discussion总结了以下几种解法:

1. 神秘的数学特性

不停地计算,如果出现了4那么就肯定不快乐,而出现了1就是快乐。为什么出现4就不快乐呢?

4*4=16;1*1+6*6=37;3*3+7*7=58;5*5+8*8=89;8*8+9*9=145;1*1+4*4+5*5=42;4*4+2*2=20;2*2+0*0=4;无限循环。

具体大佬们是怎么发现这个规律的,以及它为什么是正确的,我就不追究了,总之得到这个规律就好,其实重点还是在后面几种做法上。

这个解法的时间复杂度我猜是O(n^2)因为有两个loop?空间复杂度O(1)。代码运行时间0ms,空间8.2M,都打败了100%:

class Solution {
public:
    int digitSquareSum(int n) {
        int result = 0;
        while (n) {
            result += (n % 10) * (n % 10);
            n /= 10;
        }
        return result;
    }
    bool isHappy(int n) {
        if (n <= 0) {
            return false;
        }
        while (true) {
            if (n == 1) {
                return true;
            }
            else if (n == 4) {
                return false;
            }
            n = digitSquareSum(n);
        }
        return true;
    }
};

2022.11.8

这次是自己写出来了递归,递归的终止条件是if n < 4,因为3^2 = 9,所以只要大于3的都会会到只有一位数的情况(虽然这也只是直觉,我并没有严格的数学证明)。但是忘了4的这种情况。于是终止条件改成了if n <= 4,然后加了个if n ==4 return false就好了。就,脑筋急转弯了。

class Solution {
    public boolean isHappy(int n) {
        if (n == 4) {
            return false;
        } else if (n < 4) {
            return n == 1;
        }
        int sum = 0;
        while (n != 0) {
            sum += Math.pow(n % 10, 2);
            n /= 10;
        }
        return isHappy(sum);
    }
}

2. 采用set存放已经出现过的数字平方之和

其实这个方法也需要数学证明,通过采用set来存放已经出现过的数字,如果出现了重复的数字,且这个重复的数字不是1,那么就不是happy number,算法的正确性待证明。实现出来代码的时间复杂度我猜还是O(n^2),运行时间4ms,66.16%,空间复杂度我猜是O(n),8.3M,76.92%:

class Solution {
public:
    int digitSquareSum(int n) {
        int result = 0;
        while (n) {
            result += (n % 10) * (n % 10);
            n /= 10;
        }
        return result;
    }
    bool isHappy(int n) {
        if (n <= 0) {
            return false;
        }
        set<int> results;
        while (true) {
            int sum = digitSquareSum(n);
            if (results.count(sum) != 0) {
                return sum == 1;
            }
            else {
                results.insert(sum);
                n = sum;
            }
        }
        return true;
    }
};

2022.11.8

嗯,完全忘了还能用set来存,如果出现重复就说明进循环了。但是,也得base on循环就一定不快乐的理论基础吧。不管了。循环终止条件是如果set里出现了重复的数字,否则遇到1就return true,要么就最后return false。

class Solution {
    public boolean isHappy(int n) {
        Set<Integer> set = new HashSet<>();
        while (!set.contains(n)) {
            if (n == 1) {
                return true;
            }
            set.add(n);
            n = getSquareSum(n);
        }
        return false;
    }
    private int getSquareSum(int n) {
        int sum = 0;
        while (n != 0) {
            sum += Math.pow(n % 10, 2);
            n /= 10;
        }
        return sum;
    }
}

 

3. 快慢指针

这个方法借鉴了判断链表是否存在环的技巧,但是它还是基于“经过数字平方之和计算后总会产生循环,如果循环的元素不是1就不是happy number”的这个定理之上的,算法的正确性证明是逃不掉的orz 在这里可以把这个循环看作是一个环,设置一个每次走两步的快指针和一个每次走一步的慢指针,最后两个指针总会相遇在环中。这道题中只需要判断两个指针相遇时它们指向的数字即可,如果是happy number,那么这个环应该永远是1(数学证明省略)。代码如下,运行时间4ms,66.16%,空间7.9M,100%:

class Solution {
public:
    int digitSquareSum(int n) {
        int result = 0;
        while (n) {
            result += (n % 10) * (n % 10);
            n /= 10;
        }
        return result;
    }
    bool isHappy(int n) {
        if (n <= 0) {
            return false;
        }
        int fast = digitSquareSum(digitSquareSum(n));
        int slow = digitSquareSum(n);
        while (fast != slow) {
            fast = digitSquareSum(digitSquareSum(fast));
            slow = digitSquareSum(slow);
        }
        return fast == 1;
    }
};

这道题的数学证明暂时不想看,留个坑以后填。

2022.11.8

就,还是没有想到快慢指针的做法,以及想到了也不知道怎么给它应用到linkedlist的方法上。最后看了答案还是debug了半天,主要是卡在了n = 1和n = 10这种情况,我的while条件是slow != fast,但是这两种情况都是slow == fast = 1,所以就不会进入循环,直接到外层的return,所以外层return的时候就return slow == 1就行。

class Solution {
    public boolean isHappy(int n) {
        int fast = getSquareSum(getSquareSum(n));
        int slow = getSquareSum(n);
        while (fast != slow) {
            if (fast == 1) {
                return true;
            }
            fast = getSquareSum(getSquareSum(fast));
            slow = getSquareSum(slow);
        }
        return slow == 1;
    }
    private int getSquareSum(int n) {
        int sum = 0;
        while (n != 0) {
            sum += Math.pow(n % 10, 2);
            n /= 10;
        }
        return sum;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值