一文透彻负数与整除技巧!LeetCode 263「丑数」两大解法多语言题解

一、题目简介

LeetCode 263(实际上在力扣国际站为 263)《丑数》(Ugly Number)是一道经典的数论题,考查对因子分解和递归或迭代判断的方法。丑数定义为只包含质因子 2、3、5 的正整数。给定一个整数 n,判断它是否为丑数。

二、题目描述

实现一个函数 isUgly(n),如果 n 是丑数就返回 true,否则返回 false。
1 也被视为丑数。
丑数的定义:它的所有质因子只包含 2、3、5。注意 n 可能为负数或者 0。

函数签名:

def isUgly(n: int) -> bool

三、示例分析

示例 1:

输入: n = 6
输出: true
解释:6 = 2 × 3

示例 2:

输入: n = 8
输出: true
解释:8 = 2 × 2 × 2

示例 3:

输入: n = 14
输出: false
解释:14 = 2 × 7,其中 7 不是允许质因子

示例 4:

输入: n = 1
输出: true
解释:1 被视为丑数

示例 5:

输入: n = 0
输出: false
解释:0 不是正整数,故不是丑数

四、解题思路与详细步骤

方案一:迭代除尽法(贪心消除质因子)

步骤详解
  1. 如果 n ≤ 0,直接返回 False(非正整数)。
  2. 对 n 不断除以 2、3、5:
    • 当 n % 2 == 0,n //= 2;
    • 当 n % 3 == 0,n //= 3;
    • 当 n % 5 == 0,n //= 5;
      重复这三步直到 n 无法被 2、3、5 整除为止。
  3. 最后判断如果 n == 1,则说明原数仅包含 2、3、5 质因子,是丑数;否则不是。
优缺点
  • 时间复杂度 O(log n),每次消除至少会将 n 除以 2、3 或 5,快速缩小 n。
  • 空间 O(1),不需要额外存储。
  • 思路直观,面试常用。

方案二:递归除尽法

步骤详解
  1. 递归函数 check(n)
    • 如果 n == 1,返回 True;
    • 如果 n ≤ 0 或 n 不能被 2、3、5 整除,则返回 False;
    • 如果 n % 2 == 0,返回 check(n // 2)
    • 否则如果 n % 3 == 0,返回 check(n // 3)
    • 否则返回 check(n // 5)
  2. 入口调用 isUgly(n) 时先判断 n ≤ 0 返回 False,否则调用 check(n)
优缺点
  • 与迭代思路等价,但使用递归实现更符合分解思路。
  • 递归深度可能较大,但 n 会快速缩小,一般不会超出递归栈深度限制。
  • 思路简洁,但需注意递归开销。

五、代码实现(Python/Java/C++)

Python 实现

解法一:迭代除尽
class Solution:
    def isUgly(self, n: int) -> bool:
        # 丑数定义只包含质因子 2, 3, 5,n 必须为正整数
        if n <= 0:
            return False
        # 不断除以 2、3、5
        for p in (2, 3, 5):
            while n % p == 0:
                n //= p
        # 最终若为 1,则只包含 2/3/5,否则包含其他因子
        return n == 1
解法二:递归除尽
class Solution:
    def isUgly(self, n: int) -> bool:
        # 非正数不是丑数
        if n <= 0:
            return False
        return self._check(n)

    def _check(self, x: int) -> bool:
        # 如果缩减到 1,说明消除干净
        if x == 1:
            return True
        # 如果不能被 2/3/5 整除,则包含其他因子
        if x % 2 != 0 and x % 3 != 0 and x % 5 != 0:
            return False
        # 优先除以 2
        if x % 2 == 0:
            return self._check(x // 2)
        # 再除以 3
        if x % 3 == 0:
            return self._check(x // 3)
        # 否则除以 5
        return self._check(x // 5)

Java 实现

解法一:迭代除尽
class Solution {
    public boolean isUgly(int n) {
        // 丑数必须为正
        if (n <= 0) return false;
        // 不断除以 2, 3, 5
        for (int p : new int[]{2, 3, 5}) {
            while (n % p == 0) {
                n /= p;
            }
        }
        // n 缩减到 1 则为丑数
        return n == 1;
    }
}
解法二:递归除尽
class Solution {
    public boolean isUgly(int n) {
        if (n <= 0) return false;
        return check(n);
    }
    private boolean check(int x) {
        if (x == 1) return true;
        if (x % 2 != 0 && x % 3 != 0 && x % 5 != 0) return false;
        if (x % 2 == 0) return check(x / 2);
        if (x % 3 == 0) return check(x / 3);
        return check(x / 5);
    }
}

C++ 实现

解法一:迭代除尽
class Solution {
public:
    bool isUgly(int n) {
        // 非正数直接返回 false
        if (n <= 0) return false;
        // 消除所有 2, 3, 5 因子
        for (int p : {2, 3, 5}) {
            while (n % p == 0) {
                n /= p;
            }
        }
        // 最终若为 1,则仅包含 2/3/5
        return n == 1;
    }
};
解法二:递归除尽
class Solution {
public:
    bool isUgly(int n) {
        if (n <= 0) return false;
        return check(n);
    }
private:
    bool check(int x) {
        if (x == 1) return true;
        if (x % 2 != 0 && x % 3 != 0 && x % 5 != 0) return false;
        if (x % 2 == 0) return check(x / 2);
        if (x % 3 == 0) return check(x / 3);
        return check(x / 5);
    }
};

六、复杂度分析

  • 解法一(迭代)

    • 时间复杂度 O(log n),因为 n 每次至少除以 2/3/5,位数快速减少。
    • 空间复杂度 O(1),只使用常数级额外空间。
  • 解法二(递归)

    • 时间复杂度 O(log n),同理。
    • 空间复杂度 O(log n),取决于递归深度(递归栈空间)。

七、边界与细节注意

  1. n ≤ 0 必须返回 false,包括 0 和负数。
  2. n == 1 视为丑数,直接返回 true。
  3. 在迭代除尽时注意:
    • 每次 while (n % p == 0)n /= p,以消除所有该质因子。
    • 若 n 最终等于 1,则说明只包含 2、3、5;否则存在其他质因子。

八、模拟面试环节及答案

1. 问:为什么只需要除尽 2、3、5?

答:
丑数定义为只包含质因子 2、3、5。任何其他质因子在除净 2/3/5 后如果 n > 1,则说明有不允许的因子。

2. 问:递归方案会造成栈溢出吗?

答:
n 每次至少除以 2,递归深度约为 log₂(n),对于 n 在32位范围内,深度极小,不会栈溢出。但若 n 极大(比如 10¹⁸),需考虑迭代方案更稳。

3. 问:哈希或集合能做吗?

答:
可以先不断记录除尽后的因子后判断是否剩余 1。但本题直接位运算/取模法更简单高效,不需要额外结构。

4. 问:如何拓展到其他质因子集合?

答:
只需调整除尽质因子集合,例如若质因子为 {2,3,7},则在迭代中循环除以这三种质因子。

5. 问:负丑数的定义是什么?

答:
题目仅针对正整数定义丑数。若允许负数,可在 n < 0 时先取绝对值,然后再判断。但本题要求 n ≤ 0 返回 false。


九、总结升华

丑数问题考查对质因子分解、循环或递归的掌握。掌握这一方法后,还可延伸到“下一个丑数”(LeetCode 264)等动态规划题目,以及其他只含特定质因子的数列判定,是面试和基础刷题库的必修课题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数据分析螺丝钉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值