LeetCode 202. 快乐数

202. 快乐数

题目:编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」 定义为:
对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n 是 快乐数 就返回 true ;不是,则返回 false 。

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

个人思路
  1. 求取平方和:不断取余,然后进行平方相加;判断平方和temp是否为1,是则返回True,同时建立一个memory列表保存每一次的结果,判断temp是否在前面平方和中出现过,即判断是否会出现循环,写代码的过程中出现下面的情况:
memory = []
class Solution:
    def isHappy(self, n: int) -> bool:
        temp = 0     
        while n > 0:
            temp = temp + (n % 10)**2
            n = n // 10
        if temp == 1:
            return True
        elif temp in memory:
            return False
        else:
            memory.append(temp)
            self.isHappy(temp)

自己测试的时候打印发现结果应该是对的,但却没有任何返回值
原因:该函数为递归函数,else中嵌套函数。 执行return满足条件if temp == 1不是最外面的循环,而是else self.isHappy(temp)中 执行的函数,因此return返回的值被返回到了else中,不会返回到最外面一层。
解决:在else中也增加return。
但还是出现了错误,后面有个用例没能通过

memory = []
class Solution:
    def isHappy(self, n: int) -> bool:
        temp = 0     
        while n > 0:
            temp = temp + (n % 10)**2
            n = n // 10
        if temp == 1:
            return True
        elif temp in memory:
            return False
        else:
            memory.append(temp)
            return self.isHappy(temp)

因为存储temp是否在之前中出现过的列表memory在class外面,猜测是memory把前面其他用例计算过程中的temp也储存了下来,但不知道怎么修改:放在Solution后isHappy前会报错memory未定义,放在方法内则递归的时候每次都是空,也起不到作用。
正确的递归,在原有的方法内再新建一个方法:

class Solution:
    def isHappy(self, n: int) -> bool:          
        memory = []
        def happy(n):
            temp = 0   
            while n > 0:
                temp = temp + (n % 10)**2
                n = n // 10
            if temp == 1:
                return True
            elif temp in memory:
                return False
            else:
                memory.append(temp)
                return happy(temp)
        return happy(n)

注:memory = set()这个更快

官方思路
  1. 用哈希集合检测循环
    我们使用哈希集合而不是向量、列表或数组的原因是因为我们反复检查其中是否存在某数字。检查数字是否在哈希集合中需要 O(1) 的时间,而对于其他数据结构,则需要 O(n) 的时间。选择正确的数据结构是解决这些问题的关键部分
def isHappy(self, n: int) -> bool:

    def get_next(n):
        total_sum = 0
        while n > 0:
            n, digit = divmod(n, 10)
            total_sum += digit ** 2
        return total_sum

    seen = set()
    while n != 1 and n not in seen:
        seen.add(n)
        n = get_next(n)

    return n == 1

这里的时间复杂度以及后面的思路分析有点意思,有兴趣的可以点击下面的链接自行查看

  1. 快慢指针法
    (1)通过反复调用 getNext(n) 得到的链是一个隐式的链表。隐式意味着我们没有实际的链表节点和指针,但数据仍然形成链表结构。起始数字是链表的头 “节点”,链中的所有其他数字都是节点。next 指针是通过调用 getNext(n) 函数获得。
    (2)意识到我们实际有个链表,那么这个问题就可以转换为检测一个链表是否有环。因此我们在这里可以使用弗洛伊德循环查找算法。这个算法是两个奔跑选手,一个跑的快,一个跑得慢。在龟兔赛跑的寓言中,跑的慢的称为 “乌龟”,跑得快的称为 “兔子”。
    (3)不管乌龟和兔子在循环中从哪里开始,它们最终都会相遇。这是因为兔子每走一步就向乌龟靠近一个节点(在它们的移动方向上)。
    (4)算法:我们不是只跟踪链表中的一个值,而是跟踪两个值,称为快跑者和慢跑者。在算法的每一步中,慢速在链表中前进 1 个节点,快跑者前进 2 个节点(对 getNext(n) 函数的嵌套调用)。
    如果 n 是一个快乐数,即没有循环,那么快跑者最终会比慢跑者先到达数字 1。
    如果 n 不是一个快乐的数字,那么最终快跑者和慢跑者将在同一个数字上相遇。
def isHappy(self, n: int) -> bool:  
    def get_next(number):
        total_sum = 0
        while number > 0:
            number, digit = divmod(number, 10)
            total_sum += digit ** 2
        return total_sum

    slow_runner = n
    fast_runner = get_next(n)
    while fast_runner != 1 and slow_runner != fast_runner:
        slow_runner = get_next(slow_runner)
        fast_runner = get_next(get_next(fast_runner))
    return fast_runner == 1

一些思考:
① 本来认为这个解答有缺陷,因为快指针每次走两步,会漏掉走到1的一步,细看发现,漏掉的那一步如果为1,即get_next(fast_runner)为1,那么get_next(get_next(fast_runner))还是会为1,其实不影响
② 以为存在快指针刚好跳过慢指针从而恰好错过相遇,龟兔一直循环跑的情形,其实不然。在兔快要遇上龟时,有两种情况,兔与龟之间相差一个空位,这时,它们将在下一次走动相遇;如果相差两个空位,那么走动一次后将相差一个空位,即到了前面的情况。

时间复杂度:O(logn)
空间复杂度:O(1)

  1. 数学
    (1)下一个值可能比自己大的最大数字是什么?根据我们之前的分析,我们知道它必须低于 243。因此,我们知道任何循环都必须包含小于 243 的数字,用这么小的数字,编写一个能找到所有周期的强力程序并不困难。
    (2)如果这样做,您会发现只有一个循环:4→16→37→58→89→145→42→20→4。所有其他数字都在进入这个循环的链上,或者在进入 1 的链上。
    (3)因此,我们可以硬编码一个包含这些数字的散列集,如果我们达到其中一个数字,那么我们就知道在循环中。
def isHappy(self, n: int) -> bool:

    cycle_members = {4, 16, 37, 58, 89, 145, 42, 20}

    def get_next(number):
        total_sum = 0
        while number > 0:
            number, digit = divmod(number, 10)
            total_sum += digit ** 2
        return total_sum

    while n != 1 and n not in cycle_members:
        n = get_next(n)

    return n == 1

这样的题目还是简单题,而且名字还叫快乐数,我哭死

作者:LeetCode-Solution
链接:https://leetcode.cn/problems/happy-number/solution/kuai-le-shu-by-leetcode-solution/

你可能感兴趣的:(指针使用,哈希表/备忘录算法,数学,leetcode,算法,职场和发展)