一、题目
编写一个程序,找出第 n 个丑数。
丑数就是质因数只包含 2, 3, 5 的正整数。
示例:
输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。
说明:
- 1 是丑数。
- n 不超过1690。
二、解决
1、优先级队列
思路:
丑数性质:丑数 = 某较小丑数 * 某因子
过程:从1开始,乘2、3、5后,取最小结果,再乘。循环至数量达到 n n n。
代码:
class Solution {
public int nthUglyNumber(int n) {
if(n==1) return 1;
PriorityQueue<Long> q = new PriorityQueue();
q.add(1l);
for(long i=1; i<n; i++) {
long tmp = q.poll();
while(!q.isEmpty() && q.peek()==tmp) tmp = q.poll(); // 去除重复的丑数
q.add(tmp*2);
q.add(tmp*3);
q.add(tmp*5);
}
return q.poll().intValue();
}
}
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
n
)
O(n)
O(n)
2、动态规划
思路:
1、状态定义
dp[i]:第i+1个丑数。
2、转移方程
dp[i+1] = min {dp[p2]*2, dp[p3]*3, dp[p5]*5}
3、初始值dp[0]=1,返回值dp[n-1]
代码:
class Solution {
public int nthUglyNumber(int n) {
int[] dp = new int[n+1]; // dp[i] holds the ith's ugly number
dp[1] = 1;
int p2 = 1, p3 = 1, p5 = 1;
for (int i=2; i<=n; i++) { // loop invariant:dp[i] holds the smallest ith uglynumber
dp[i] = Math.min(2*dp[p2], Math.min(3*dp[p3],5*dp[p5])); // the next ugly number must be built from a smaller ugly number
if (dp[i] == 2*dp[p2]) p2++;
if (dp[i] == 3*dp[p3]) p3++;
if (dp[i] == 5*dp[p5]) p5++;
}
return dp[n];
}
}
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
n
)
O(n)
O(n)
三、参考
1、面试题49. 丑数(动态规划,清晰图解)
2、丑数 II
3、你绝对能轻松看得懂的丑数解题思路
4、Java solution – using PriorityQueue
5、Shortest O(n) Java DP solution