返回

为你揭开丑数的真面目:从生成原理到求解捷径!

见解分享

在数字王国里,存在着一种独特的数字物种——丑数。它们身上只能带有 2、3、5 这三位质数的基因。而我们今天要探秘的,就是如何找到第 n 个丑数,这位丑数家族中的第 n 位成员。

丑数的诞生

丑数的生成原理并不复杂,它们就像搭积木一样,由这三位质数因子不断组合而成。以第 1 个丑数为例,它就是最朴素的 1,不含任何质因数。而第 2 个丑数则由 2 的质因数诞生,为 2。以此类推,丑数家族逐个诞生。

排序求解:直截了当

要找到第 n 个丑数,最直接的方法莫过于对所有丑数进行排序,然后从中找出第 n 位的成员。这就好比我们参加一场赛跑,要获得第 n 名,就得先把所有参赛选手按成绩排个序。

捷径寻数:效率飞扬

排序求解虽然直观,但效率却并不高。有没有什么捷径能让我们更快地找到第 n 个丑数呢?答案是肯定的!

想象一下,我们现在有三条流水线,每条流水线上都有一个丑数生成器,分别负责生成以 2、3、5 为质因子的丑数。每条流水线上的丑数都按从小到大的顺序排列。那么,要找到第 n 个丑数,我们就只需要从这三条流水线上各取一个丑数,然后比较它们的大小,找出最小的那个。这个最小的丑数,就是我们想要的第 n 个丑数。

为什么这个方法奏效呢?因为我们从每条流水线上取的丑数,都是这些流水线上未被选取的丑数中最小的。因此,这三个丑数中,必定有一个是最小的,也就是第 n 个丑数。

代码实现:清晰简洁

掌握了捷径原理,编写代码就是水到渠成的事了。我们只需要用一个数组来存储这三条流水线上的丑数,然后不断比较它们的大小,就能找到第 n 个丑数。

def nthUglyNumber(n):
    if n <= 0:
        return 0

    dp = [0] * n  # dp[i]表示第i个丑数

    # 初始化三条流水线上的丑数
    i2, i3, i5 = 0, 0, 0
    dp[0] = 1  # 第一个丑数为1

    # 循环得到第n个丑数
    for i in range(1, n):
        n2, n3, n5 = dp[i2] * 2, dp[i3] * 3, dp[i5] * 5
        dp[i] = min(n2, n3, n5)  # 取最小的丑数

        # 更新流水线上的丑数
        if dp[i] == n2:
            i2 += 1
        if dp[i] == n3:
            i3 += 1
        if dp[i] == n5:
            i5 += 1

    return dp[n - 1]

LeetCode 264:巧用捷径

有了这个捷径,我们就可以轻松应对 LeetCode 264 难题了。

输入:n = 10

输出:12

解释:丑数序列的前 10 个数字为 [1, 2, 3, 4, 5, 6, 8, 9, 10, 12],第 10 个丑数为 12。

使用我们刚才的捷径方法,只需要计算出前 10 个丑数,即可得到第 10 个丑数为 12。

结语

丑数的求解,看似复杂,但只要掌握其生成原理,并灵活运用捷径,就能轻松找到第 n 个丑数。希望这篇文章能为你拨开丑数的迷雾,让你在数字王国里畅游无阻!