题目详解
我们把只包含因子2, 3 和 5的数称作为丑数(Ugly Number)。 求按从小到大的顺序的第n个丑数
输入:n = 10
输出:12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。
暴力解法
最简单的思路是暴力了!!!把所有在范围内的丑数都列举出来,然后去重+排序。然而妥妥的超出时间限制。
class Solution(object):
def nthUglyNumber(self, n):
Set = set()
for i in range(n):
for j in range(int(math.log(pow(2,n-i),3))+1):
for k in range(int(math.log(pow(2,n-i)//pow(3,j),5))+1):
x = pow(2,i)*pow(3,j)*pow(5,k)
Set.add(x)
List = list(Set)
List.sort()
return List[n-1]
动态规划+ 二分法
设立一个数组dp,假设里面保存了N个排好序的丑数。那么第N+1个丑数就必定来自于dp * 2 , dp * 3, dp * 5中产生的。比如说有丑数序列dp[1, 2 , 3, 5 ] ,那么第N+1个丑数肯定在这三个数组中,找到这三个数组中大于5的最小数。三个数组得到的分别是[6, 6,10],由此而知下一个数就是6了。总的时间复杂度为O(N^2)或者O(NlogN)。
Num2 = [ 1*2, 2*2, 3*2, 5*2]
Num3 = [ 1*3, 2*3, 3*3, 5*3]
Num5 = [ 1*5, 2*5, 3*5, 5*5]
动态规划+ 三指针
上面的方法做了很多重复的工作,由于dp前面的数是固定不变的,没必要每次都乘以2, 3, 5。
- 我们只需要保存p2, p3, p5这三个指针,三个指针分别代表该丑数等待被 * 2 ,* 3, * 5, 或者说是该丑数是否通过* 2, * 3 ,* 5产生过新丑数的标识,每个数三个指针三个标识,用完即用来白标识别的数,就这三个标识一直复用。)
- 当一个丑数已经被* 2, * 3, * 5后,对于生成丑数已经没有用了,我们把对应指针前移一位,让下一个丑数等待被*来生成新丑数。
- 每次计算出来个三个丑数的最小的一个作为新丑数加入,然后判断是通过 * 多少得到的来后移对应指针。
通过123我们可以发现,我们每次生成的都是最小的丑数(保证是升序),并且每个丑数都尝试过被 *2 *3 *5(保证无遗漏)。
class Solution(object):
def nthUglyNumber(self, n):
p2 = 0
p3 = 0
p5 = 0
List = [1]
while len(List)<n:
List.append(min(List[p2]*2,List[p3]*3,List[p5]*5))
# 这里是用if, if, if。 而不是if else。 用来去重
if List[p2]*2==List[-1]:
p2 +=1
if List[p3]*3==List[-1]:
p3 += 1
if List[p5]*5==List[-1]:
p5 += 1
return List[n-1]