最简分数

标签: 数学 字符串 数论

难度: Medium

给你一个整数 n ,请你返回所有 0 到 1 之间(不包括 0 和 1)满足分母小于等于  n 的 最简 分数 。分数可以以 任意 顺序返回。

示例 1:

输入:n = 2
输出:["1/2"]
解释:"1/2" 是唯一一个分母小于等于 2 的最简分数。

示例 2:

输入:n = 3
输出:["1/2","1/3","2/3"]

示例 3:

输入:n = 4
输出:["1/2","1/3","1/4","2/3","3/4"]
解释:"2/4" 不是最简分数,因为它可以化简为 "1/2" 。

示例 4:

输入:n = 1
输出:[]

提示:

  • 1 <= n <= 100

Submission

运行时间: 48 ms

内存: 16.5 MB

class Solution:
    def simplifiedFractions(self, n: int) -> List[str]:
        return [f"{numerator}/{denominator}" for denominator in range(2, n + 1) for numerator in range(1, denominator) if gcd(denominator, numerator) == 1]

Explain

该题解使用了列表推导来构建所有可能的最简分数。对于每个分母denominator从2到n,尝试每个分子numerator从1到denominator-1。通过使用gcd函数(最大公约数函数)检查分子和分母是否互质(gcd等于1),来确定分数是否最简。只有当gcd(denominator, numerator)为1时,分数'numerator/denominator'才是最简分数并被添加到结果列表中。

时间复杂度: O(n^2 * log(n))

空间复杂度: O(n^2)

class Solution:
    def simplifiedFractions(self, n: int) -> List[str]:
        # 列表推导来生成所有可能的最简分数
        return [f"{numerator}/{denominator}" for denominator in range(2, n + 1) for numerator in range(1, denominator) if gcd(denominator, numerator) == 1]

Explore

在生成最简分数时,分子的范围是从1到denominator-1,而不包括denominator本身,是因为如果分子等于分母(即numerator = denominator),则分数就成了1,这不是一个真分数(真分数的定义是分子小于分母的分数)。此外,这样的分数(如1/1, 2/2, 3/3等)也不是最简分数,因为它们可以进一步简化为1。因此,为了确保每个分数都是真分数并且是最简的,分子的取值范围必须是小于分母的正整数。

列表推导提供了一种更简洁和直观的方式来构建列表,尤其是在需要应用某些条件过滤元素时。在这个算法中,使用列表推导可以直接在一个表达式中完成迭代、条件判断和元素构造,使代码更加清晰和简短。从效率上讲,列表推导通常比使用普通的循环结构更高效,因为它是优化过的C语言内部实现,这使得它在迭代和元素添加过程中执行得更快。然而,列表推导的劣势在于当逻辑变得复杂,包含多重循环和多个条件判断时,其代码的可读性可能会下降,且对于非常大的数据集,列表推导可能会消耗大量内存,因为它会立即生成整个列表。

该题解确实考虑了包括n=1在内的所有边界条件。当n=1时,由于分母的范围是从2到n,这意味着没有任何分母可以选择(因为最小的分母是2),因此不会有任何分数被生成。这是符合逻辑的,因为分数需要一个大于1的分母才能形成。因此,对于n=1,输出是空数组,这表明在这种情况下不存在任何最简分数。