返回

算法突破:如何找到第 n 个丑数

后端

一、丑数的定义和特点

丑数是指仅包含质因子 2、3 和 5 的正整数,如 6、8、10、14 等都是丑数。丑数的生成存在一些有趣的特点:

  • 1 是最小的丑数。
  • 每个丑数都必须由一个更小的丑数乘以 2、3 或 5 得到。
  • 对于任意一个正整数 n,存在一个唯一的丑数为第 n 个丑数。

二、算法详解:寻找第 n 个丑数

为了找出第 n 个丑数,我们可以借助动态规划的思想来构建一个算法。具体步骤如下:

  1. 初始化:创建一个数组 dp,其中 dp[i] 表示第 i 个丑数。dp[0] 被定义为 1,因为 1 是最小的丑数。

  2. 初始化三个指针 p2、p3 和 p5,分别指向 dp 数组中包含质因子 2、3 和 5 的最小值的索引。

  3. 从 i = 1 开始,依次生成第 i 个丑数:

    • 计算三个指针指向的丑数的最小值,并将其存储在 dp[i] 中。
    • 如果 dp[i] 可以被 2 整除,则将 p2 加 1,表示已经找到了包含质因子 2 的下一个最小丑数。
    • 如果 dp[i] 可以被 3 整除,则将 p3 加 1,表示已经找到了包含质因子 3 的下一个最小丑数。
    • 如果 dp[i] 可以被 5 整除,则将 p5 加 1,表示已经找到了包含质因子 5 的下一个最小丑数。
  4. 重复步骤 3,直到生成第 n 个丑数。

这种算法的时间复杂度为 O(n),空间复杂度为 O(n)。

三、代码实现

以下是用 Python 实现的算法代码:

def ugly_number(n):
  """
  返回第 n 个丑数。
  """
  # 初始化数组 dp 和三个指针 p2、p3 和 p5
  dp = [0] * n
  dp[0] = 1
  p2 = p3 = p5 = 0

  # 从 i = 1 开始生成丑数
  for i in range(1, n):
    # 计算三个指针指向的丑数的最小值
    next_ugly_number = min(dp[p2] * 2, dp[p3] * 3, dp[p5] * 5)

    # 将最小值存储在 dp[i] 中
    dp[i] = next_ugly_number

    # 如果 dp[i] 可以被 2 整除,则将 p2 加 1
    if next_ugly_number == dp[p2] * 2:
      p2 += 1

    # 如果 dp[i] 可以被 3 整除,则将 p3 加 1
    if next_ugly_number == dp[p3] * 3:
      p3 += 1

    # 如果 dp[i] 可以被 5 整除,则将 p5 加 1
    if next_ugly_number == dp[p5] * 5:
      p5 += 1

  # 返回第 n 个丑数
  return dp[n - 1]


# 测试代码
n = 10
print(ugly_number(n))

四、算法的应用

丑数在计算机科学中有很多应用,例如:

  • 查找第 n 个丑数可以用于生成丑数序列,用于测试算法的正确性。
  • 丑数可以用于设计哈希表,提高哈希表的性能。
  • 丑数可以用于设计堆排序算法,提高堆排序的性能。

五、结语

希望本文对您理解丑数的定义和特点以及如何使用动态规划算法找到第 n 个丑数有所帮助。如果您对算法或其他计算机科学问题有任何疑问,欢迎随时与我讨论。