给你一个整数 n ,请你找出并返回第 n 个 丑数 。
丑数 就是只包含质因数 2、3 和/或 5 的正整数。
解题思路
这一题可以利用动态规划,定义一个数组dp[], dp[i-1],表示第i个丑数,由于最小丑数为1,所以dp[0] = 1。
设三个指针分别指向dp[0]位置 p2 = 0, p3 = 0, p5 = 0,利用3指针获取他们不同的合数,每个指针都取所对应数组中的质因数让它与 2,3,5 相乘取最小合数作为下一个位置的值,当遇到合数的质因数为2,3,5三个指针时,质因数的指针位数+1
示例1 n = 10
1
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
p2
p3
p5
// dp[1]的合数为2,3,5,取最小数2,因为2是合数的质因数,所以p2往前移一位
1 2
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
p2
p3
p5
2 * dp[p2] = 4
3 * dp[p3] = 3
5 * dp[p5] = 5
// dp[2]的合数为4,3,5,取最小数3,因为3是合数的质因数,所以p3往前移一位
1 2 3
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
p2
p3
p5
2 * dp[p2] = 4
3 * dp[p3] = 6
5 * dp[p5] = 5
// dp[2]的合数为4,6,5,取最小数4,因为2是合数的质因数,所以p2往前移一位
1 2 3 4
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
p2
p3
p5
2 * dp[p2] = 6
3 * dp[p3] = 6
5 * dp[p5] = 5
// dp[2]的合数为6,6,5,取最小数5,因为5是合数的质因数,所以p5往前移一位
1 2 3 4 5
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
p2
p3
p5
2 * dp[p2] = 6
3 * dp[p3] = 6
5 * dp[p5] = 10
// dp[2]的合数为6,6,10,取最小数5,因为2, 3是合数的质因数,所以p2,p3往前移一位
1 2 3 4 5 6
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
p2
p3
p5
2 * dp[p2] = 8
3 * dp[p3] = 9
5 * dp[p5] = 10
// dp[2]的合数为8,9,10,取最小数8,因为2是合数的质因数,所以p2往前移一位
1 2 3 4 5 6 8
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
p2
p3
p5
2 * dp[p2] = 10
3 * dp[p3] = 9
5 * dp[p5] = 10
// dp[2]的合数为10,9,10,取最小数9,因为3是合数的质因数,所以p3往前移一位
1 2 3 4 5 6 8 9
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
p2
p3
p5
2 * dp[p2] = 10
3 * dp[p3] = 12
5 * dp[p5] = 10
// dp[2]的合数为10,12,10,取最小数10,因为2,5是合数的质因数,所以p2,p5往前移一位
1 2 3 4 5 6 8 9 10
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
p2
p3
p5
2 * dp[p2] = 12
3 * dp[p3] = 12
5 * dp[p5] = 15
// dp[2]的合数为12,12,15,取最小数12,因为2,3是合数的质因数,所以p2,p3往前移一位
1 2 3 4 5 6 8 9 10 12
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
p2
p3
p5
最后得到第10个整数为12
代码
var nthUglyNumber = function(n) {
var dp = new Array();
dp[0] = 1;
var p2 = 0,
p3 = 0,
p5 = 0;
for (let i = 1; i < n; i++) {
dp[i] = Math.min(dp[p2] * 2, Math.min(dp[p3] * 3, dp[p5] * 5));
if (dp[i] === dp[p2] * 2) p2++;
if (dp[i] === dp[p3] * 3) p3++;
if (dp[i] === dp[p5] * 5) p5++;
}
return dp[n-1];
};