Ugly-Number-series
Last updated
Was this helpful?
Last updated
Was this helpful?
本章分为两个部分:
测试用例:
编写一个程序,找出第 n 个丑数。 丑数就是质因数只包含 2, 3, 5 的正整数。
测试用例:
示例: 输入: n = 10 输出: 12 解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。
说明: 1 是丑数。 n 不超过1690。
这道题有两个思路,分别为动态规划和使用小根堆。
小根堆:
我们可以从第一个丑数开始,将最小的丑数分别乘以2、3或者5。这样新求出3个丑数入堆。然后再从堆中选择一个最小继续重复乘以2、3、5的操作。直到重读这个操作n次。
使用小根堆的原因是因为它能够帮助对求出的丑数进行排序,从而总是能够选出最小的丑数。但是新求的丑数有可能重复。例如丑数6,可以通过2X3得到,也可以通过3X2得到。这都是有可能发生的,所以从堆中弹出最小的丑数x后,要看看新的栈顶是否与x相同,如果相同,则需要将新的丑数弹出直至二者不等。
动态规划
小根堆是将丑数插入到结果集后再排序,而动态规划的方法时在在求出三个新丑数后,我们自己排序找到最小的丑数后插入结果集中。
我们可以声明一个一维dp数组,dp[i]表示第i个丑数。并且我们需要维护三个指针,p2、p3、p5。分别表示指向需要乘2的丑数,需要乘3的丑数,需要乘5的丑数。而指针的移动规则就有点巧妙了:如果新求出的丑数是通过p2指针指向的丑数求出来的,那么就将p2++,如果新求出的丑数是通过p3指针指向的丑数求出的,那么就将p3++。p5移动的规则类似。这是为什么呢?这里举个例子:
刚开始,我们只有一个丑数,p2=0,p3=0,p5=0,都指向1
这丑数。那么新求的最小丑数dp[1]=Math.min(dp[p2]*2,dp[p3]*3,dp[p5]*5)=2
。那么我们就将p2++。因为这表示1这个丑数再也不参与求新丑数的操作了,就将p2++。然后可以参与求新丑数的丑数包括1、2。那么我们就需要对这两个数分别乘以2、3、5从而才能决定最小的丑数到底是谁。
但是,2*3>1*3的,2*5>1*5。所以对于2这个丑数,它根本不用乘以3或者5,因为这些工作都由前面的丑数做了。
这动态规划的做法,这...没做过谁想得到啊!