编写一个程序,找出第 n 个丑数。
丑数就是只包含质因数 2, 3, 5 的正整数。
示例:
输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。
说明:
- 1 是丑数。
- n 不超过1690。
一、思路
(一)、动态规划
我们先来看看,第i个丑数是怎么产生。
- 首先,除了1之外的丑数都源于某个丑数乘以2,3,5,因此第i个丑数也肯定来源于此
例如:丑数2源于丑数1 * 2,丑数8源于丑数4 * 2。等等诸如此类 - 现在的知道了,丑数的生成方法,那么能否直接按序生成第i个丑数呢?
答案应该是肯定的。
但是实现起来需要解决一个难点,举个例子来说(我们用dp[i]表示:第i个丑数):
当你要计算dp[i]的时候,它应该是比dp[i-1]大的最小丑数,要找出一个比dp[i-1]大的数,是很容易的(2 * dp[i-1]),但是确定它是最小的,则很麻烦,因为这意味着你可能要将2,3,5逐个乘以之前的丑数来获取这个值,这种办法是很笨的。
因为我们知道dp数组本身是有序的,但是我们没有办法充分利用它的有序性,其根源在于,在计算下一个丑数时,我们既不知道它所使用的丑数(dp[0]…dp[i-1]中的一个),也不知道它所使用的系数(2,3,5中的一个)
为了解决这个问题,我们可以从容易的系数入手(只有三个选择),设置三个系数指针,将对应系数乘以指针所指的丑数,来表示对应系数在当前丑数序列中所能得到的最小不重复丑数。我们在决定dp[i]的时候,只需要从这三个指针中取出对应元素乘以对应系数,然后取最小值即可。
C++代码:
class Solution {
public:
int nthUglyNumber(int n) {
if(n == 1)
return 1;
vector<int> ans = {1};
// i2指向当前:最小的能整除2的丑数(1除外)
int m = 1, i2 = 0, i3 = 0, i5 = 0;
while(m <= n){
int num2 = 2 * ans[i2], num3 = 3 * ans[i3], num5 = 5 * ans[i5];
int temp = min(min(num2, num3), num5);
if(temp == num2)
i2++;
if(temp == num3)
i3++;
if(temp == num5)
i5++;
ans.push_back(temp);
m++;
}
return ans[n - 1];
}
};