题目:
给你一个整数 n
,请你找出并返回第 n
个 丑数 。
丑数 就是质因子只包含 2
、3
和 5
的正整数。
示例 1:
输入:n = 10 输出:12 解释:[1, 2, 3, 4, 5, 6, 8, 9, 10, 12] 是由前 10 个丑数组成的序列。
示例 2:
输入:n = 1 输出:1 解释:1 通常被视为丑数。
提示:
-
1 <= n <= 1690
思路如下:
要求第 n 个丑数,我们从第一个开始。丑数 就是质因子只包含 2
、3
和 5
的正整数,所以生成的链表的每个丑数都可以表示为2的倍数/3的倍数/5的倍数 。但问题是链表从左到右数字逐渐变大,因此要确定每次需要 × 2/3/5 的哪一个数字才能获得最小丑数。
这里运用动态规划的方法:用没有乘过 2
的最小丑数乘 2
;没有乘过 3
的最小丑数乘 3
;没有乘过 5
的最小丑数乘 5
。在得到的数字中取最小,即为新的丑数。
题解如下:
class Solution:
def nthUglyNumber(self, n: int) -> int:
# 初始化一个长度为n的数组dp,所有元素初始为1,dp[i]表示第i+1个丑数
dp = [1] * n
# 初始化三个指针p2、p3、p5,分别对应质因数2、3、5的起始位置,初始都指向第0个元素
p2, p3, p5 = 0, 0, 0
# 从第1个位置(即第2个丑数)开始填充dp数组,直到第n-1个位置(即第n个丑数)
for i in range(1, n):
# 计算当前三个可能的丑数候选值
cnt2, cnt3, cnt5 = dp[p2] * 2, dp[p3] * 3, dp[p5] * 5
# 取三个候选值中的最小值作为当前的丑数
dp[i] = min(cnt2, cnt3, cnt5)
# 根据当前丑数的来源,更新对应的指针
if dp[i] == cnt2:
p2 += 1 # 如果当前丑数来自2的倍数,则移动p2指针
if dp[i] == cnt3:
p3 += 1 # 如果当前丑数来自3的倍数,则移动p3指针
if dp[i] == cnt5:
p5 += 1 # 如果当前丑数来自5的倍数,则移动p5指针
# 返回第n个丑数,即dp数组的最后一个元素
return dp[-1]
题解示例:
假设n=10,代码的执行过程如下: 初始化: dp = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] p2=0, p3=0, p5=0 生成第2个丑数(索引1): cnt2=1*2=2, cnt3=1*3=3, cnt5=1*5=5 dp[1] = min(2,3,5) = 2 更新p2为1 生成第3个丑数(索引2): cnt2=2*2=4, cnt3=1*3=3, cnt5=1*5=5 dp[2] = min(4,3,5) = 3 更新p3为1 生成第4个丑数(索引3): cnt2=2*2=4, cnt3=3*3=9, cnt5=1*5=5 dp[3] = min(4,9,5) = 4 更新p2为2 生成第5个丑数(索引4): cnt2=4*2=8, cnt3=3*3=9, cnt5=1*5=5 dp[4] = min(8,9,5) = 5 更新p5为1 生成第6个丑数(索引5): cnt2=4*2=8, cnt3=3*3=9, cnt5=5*5=25 dp[5] = min(8,9,25) = 8 更新p2为3 生成第7个丑数(索引6): cnt2=8*2=16, cnt3=3*3=9, cnt5=5*5=25 dp[6] = min(16,9,25) = 9 更新p3为2 生成第8个丑数(索引7): cnt2=8*2=16, cnt3=9*3=27, cnt5=5*5=25 dp[7] = min(16,27,25) = 16 更新p2为4 生成第9个丑数(索引8): cnt2=16*2=32, cnt3=9*3=27, cnt5=5*5=25 dp[8] = min(32,27,25) = 25 更新p5为2 生成第10个丑数(索引9): cnt2=16*2=32, cnt3=9*3=27, cnt5=25*5=125 dp[9] = min(32,27,125) = 27 更新p3为3 最终,dp数组为[1, 2, 3, 4, 5, 8, 9, 16, 25, 27],返回dp[-1]即第10个丑数27。
逻辑梳理:
-
初始化:
-
dp
数组用于存储生成的丑数序列,dp[0]
初始化为1(第一个丑数)。 -
p2
、p3
、p5
三个指针分别对应质因数2、3、5的当前候选位置,初始都指向第一个丑数(索引0)。
-
-
生成丑数:
-
从第2个丑数(索引1)开始,每次生成新的丑数时,计算三个可能的候选值(当前指针所指丑数分别乘以2、3、5)。
-
取这三个候选值中的最小值作为当前的丑数,并存入
dp
数组。 -
根据当前丑数的来源(即它等于哪个候选值),移动对应的指针到下一个位置。
-
-
返回结果:
-
当循环结束时,
dp
数组的最后一个元素即为第n个丑数。
-