1 题目描述
2 解题方法
方法1:最小堆储存数组
同时设置一个集合存放我们已经考虑过的数,每看到一个丑数n,把2n,3n,5n放入这个最小堆。
class Solution:
def nthUglyNumber(self, n: int) -> int:
lst=[1]
ret=-1
s={1}
heapq.heapify(lst)
while(n!=0):
ret=heapq.heappop(lst)
n=n-1
if(2*ret not in s):
s.add(2*ret)
heapq.heappush(lst,2*ret)
if(3*ret not in s):
s.add(3*ret)
heapq.heappush(lst,3*ret)
if(5*ret not in s):
s.add(5*ret)
heapq.heappush(lst,5*ret)
print(ret)
return ret
方法2:动态规划
方法1使用动态堆的话,会预先存储较多的丑数,导致空间复杂度较高。同时,维护最小堆的话也会导致时间复杂度较高。
我们可以使用动态规划的方法:
定义数组dp,其中dp[i]表示第i个丑数(dp[1]=1)
如何得到其他的丑数呢?我们定义三个指针p2,p3,p5,表示下一个预备丑数是当前指针指向的丑数乘以对应的质因数(初始的时候,三个指针的数值都是1)。
对于后面的i,我们令dp[i]=min(dp[p2]*2,dp[p3]*3,dp[p5]*5),然后分别比较dp[i]和dp[p2]*2,dp[p3]*3,dp[p5]*5是否相等,如果相等则将对应的指针+1。(相等说明这个pi指针代表的数已经被考虑过了(要么是被自己对应的数,要么是被其他数))
正确性证明
计算d[i]的时候,指针px的含义是使得dp[j]*x>dp[i-1]的最小下标,即j>=px的时候,dp[j]*x>dp[i-1];j<px的时候,dp[j]*x<=dp[i-1]。
所以,在计算dp[i]的时候,dp[p2]*2,dp[p3]*3,dp[p5]*5都大于dp[i-1];同时dp[p2-1]*2,dp[p3-1]*3,dp[p5-1]*5都小于等于dp[i-1]。
令dp[i]=min(dp[p2]*2,dp[p3]*3,dp[p5]*5),那么这个就是大于dp[i-1]的最小丑数。
图解
1,一开始,dp[1]=1,三个指针都指向1。
2,计算dp[2],三个指针都指向1,所以分别乘以因子后是2,3,5;2最小,p2加一,dp[2]=2。
3 计算dp[3]
4 计算dp[4]
5 计算dp[5]
class Solution:
def nthUglyNumber(self, n: int) -> int:
lst=[0,1]
p2=1
p3=1
p5=1
for i in range(2,n+1):
lst.append(min(lst[p2]*2,lst[p3]*3,lst[p5]*5))
if(lst[i]==lst[p2]*2):
p2+=1
if(lst[i]==lst[p3]*3):
p3+=1
if(lst[i]==lst[p5]*5):
p5+=1
return lst[n]