拟快慢指针追逐法判断一个正整数是否为快乐数

题目

编写一个算法来判断一个正整数 n 是不是快乐数,如果 n 是快乐数就返回 true,否则返回 false 。如下所示,19为快乐数。
在这里插入图片描述
输入:n = 19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1
如下图所示,116不是快乐数:
在这里插入图片描述

解决思路

  • 快乐数定义
    (1) 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
    (2) 然后重复这个过程直到这个数变为 1,也可能是无限循环但始终变不到 1。
    (3) 如果结果可以变为1,那么这个数就是快乐数,如果不能变为1,则不是快乐数。
  • 思路
    (1)快乐数对应单链表无环的情况,不是快乐数对应单链表有环的情况。
    (2)使用快慢指针追逐法判断链表是否有环的思想解决此题(参考这里)。
    (3)将正整数 n 分解的过程看做一个单链表,如果 n 是快乐数,则分解过程中最后一个值为1,用end表示该节点,可将该节点看做链表中的null。
    (4)如果 n 是快乐数,则链表无环,也即快慢指针不能相遇,快指针最终会指向null(上述的end节点)或null的上一个节点。
    (5)如果 n 不是快乐数,即 n 的分解过程最终得不到1,可以理解为无限分解下去都走不到end节点。此种情况对单应链表中有环的情况(无限循环最终都不能指向null)。

代码

  • C++代码
# include <stdio.h>

class Solution {
public:
    int getNext(int n) {
        if (1 == n || 0 == n) {
            return n;
        }

        int sum = 0;
        while (n) {
            sum += (n % 10) * (n % 10);
            n /= 10;
        }
        return sum;
    }

    bool isHappy(int n) {
        if (1 == n) {
            return true;
        }
        int slow = n;    // 模拟慢指针
        int quick = n;   // 模拟快指针

        do {
            slow = getNext(slow);               // 慢指针每次走1步
            quick = getNext(getNext(quick));     // 快指针每次走2步
        } while (slow != quick && quick != 1 && getNext(quick));
        return quick == 1 || getNext(quick) == 1;
    }
};


int main() {
    int test = 19;
    Solution *solution = new Solution();
    if (solution->isHappy(test)) {
        printf("is a happy num\n");
    } else {
        printf("is not a happy num\n");
    }
    return 0;
}
  • python代码
    • 代码1:
# -*- coding: utf-8 -*-
import math

# 方法1:开始时刻,快慢指针都指向头结点。运算过程中使用math.floor求商
class Solution:
    def getNext(self, n):
        if 1 == n or 0 == n:
            return n

        sum = 0
        while n:
            sum += (n % 10) * (n % 10)
            n = math.floor(n / 10)
        return sum


    def isHappy(self, n: int) -> bool:
        if 1 == n:
            return True

        slow, quick = n, n
        slow = self.getNext(slow)
        quick = self.getNext(self.getNext(quick))
        while slow != quick and quick != 1 and self.getNext(quick) != 1:
            slow = self.getNext(slow)
            quick = self.getNext(self.getNext(quick))

        return 1 == quick or 1 == self.getNext(quick)


def main():
    test = 19
    solution = Solution()
    if solution.isHappy(test):
        print('is a happy num')
    else:
        print('is not a happy num')

if __name__ == "__main__":
    main()
  • 代码2:
# -*- coding: utf-8 -*-

# 方法2:开始时刻,慢指针指向头节点,快指针指向头结点的下一个节点。运算过程中使用divmod求余
class Solution:
    def getNext(self, n):
        if 1 == n or 0 == n:
            return n

        sum = 0
        while n:
            n, a = divmod(n, 10)
            sum += a ** 2
        return sum


    def isHappy(self, n: int) -> bool:
        if 1 == n:
            return True

        slow, quick = n, self.getNext(n)
        while slow != quick and quick != 1 and self.getNext(quick) != 1:
            slow = self.getNext(slow)
            quick = self.getNext(self.getNext(quick))

        return 1 == quick or 1 == self.getNext(quick)


def main():
    test = 19
    solution = Solution()
    if solution.isHappy(test):
        print('is a happy num')
    else:
        print('is not a happy num')

if __name__ == "__main__":
    main()

python两种方法的区别:
(1)开始时刻快慢指针的位置不同,方法1中开始时刻快慢指针都指向头节点(链表的第一个节点)。方法2中,开始时刻慢指针指向头节点,快指针指向头节点的下一个节点。
(2)方法1中求商时,使用了math.floor向下取整。注意,若不进行向下取整,商可能为小数,导致结果错误。
(3)方法2中使用了divmod求商和余数,推荐使用该方法。

说明

  • 对应LeetCode第202题。
  • 链接:https://leetcode-cn.com/problems/happy-number/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值