题目:
我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。
示例:  
1 2 3
   | 输入: n = 10 输出: 12 解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。
   | 
 
方法一:最小堆
要得到从小到大的第 n 个丑数,可以使用最小堆实现。
初始时堆为空。首先将最小的丑数 1 加入堆。
每次取出堆顶元素 x,则 x 是堆中最小的丑数,由于 2x, 3x, 5x 也是丑数,因此将 2x, 3x, 5x 加入堆。
上述做法会导致堆中出现重复元素的情况。为了避免重复元素,可以使用哈希集合去重,避免相同元素多次加入堆。
在排除重复元素的情况下,第 n 次从最小堆中取出的元素即为第 n 个丑数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
   | public long nthUglyNumber2(int n) {     int[] factors = {2, 3, 5};     Set<Long> set = new HashSet<Long>();     PriorityQueue<Long> heap = new PriorityQueue<Long>();     set.add(1L);     heap.offer(1L);     long ugly = 0;     for (int i = 0; i < n; i++) {         ugly = heap.poll();         for (int factor : factors) {             long next = ugly * factor;                          if (set.add(next)) {                 heap.offer(next);             }         }     }     return ugly; }
  | 
 
复杂度分析  
- 时间复杂度:O(Nlog N)。得到第 n 个丑数需要进行 n 次循环,每次循环都要从最小堆中取出 1 个元素以及向最小堆中加入最多 3 个元素,时间复杂度是 C*O(log N),总的时间复杂度是 O(Nlog N)。
 
- 空间复杂度:O(N)。主要取决于最小堆和哈希集合的大小,最小堆和哈希集合的大小都不会超过 3n。
 
方法二:动态规划
1 2 3 4 5 6 7 8 9 10 11 12 13
   | public int nthUglyNumber3(int n) {     int a = 0, b = 0, c = 0;     int[] dp = new int[n];     dp[0] = 1;     for (int i = 1; i < n; i++) {         int n2 = dp[a] * 2, n3 = dp[b] * 3, n5 = dp[c] * 5;         dp[i] = Math.min(Math.min(n2, n3), n5);         if (dp[i] == n2) a++;         if (dp[i] == n3) b++;         if (dp[i] == n5) c++;     }     return dp[n - 1]; }
  | 
 
复杂度分析  
- 时间复杂度 O(N) : 其中 N = n,动态规划需遍历计算 dp 列表。  
 
- 空间复杂度 O(N) : 长度为 N 的 dp 列表使用 O(N) 的额外空间。
 
题目链接
poj1338