1 题目
我们把只包含因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。
示例:
输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。
说明:
1 是丑数。
n 不超过1690。
注意:本题与主站 264 题相同:https://leetcode-cn.com/problems/ugly-number-ii/
2 Java
2.1 方法一(三指针,动态规划)
核心:新丑数(大丑数)都是由2/3/5再乘一个小丑数得到
创建三个指针指向丑数列表,分别作为2/3/5的索引,指向各自的小丑数
class Solution {
public int nthUglyNumber(int n) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
if(n < 6) return list.get(n - 1);
// 新的丑数都是由2/3/5再乘一个小丑数构成
// indexA/B/C 小丑数的索引
// valueA/B/C 新丑数候选值,新丑数将从中诞生
// 选其中最小值先加入list
int indexA = 2, indexB = 1, indexC = 1;
while(list.size() < n){
int valueA = 2*list.get(indexA), valueB = 3*list.get(indexB), valueC = 5*list.get(indexC);
// 若新丑数中存在相等值,将索引向后移
if(valueA == valueB && valueA == valueC){
indexB++;
indexC++;
}
else if(valueA == valueB) indexB++;
else if(valueA == valueC) indexC++;
else if(valueB == valueC) indexC++;
// 从新丑数候选值中,选最小值加入list
valueA = 2*list.get(indexA); valueB = 3*list.get(indexB); valueC = 5*list.get(indexC);
if(valueA < valueB && valueA < valueC){
list.add(valueA);
indexA++;
}
else if(valueB < valueA && valueB < valueC){
list.add(valueB);
indexB++;
}
else if(valueC < valueA && valueC < valueB){
list.add(valueC);
indexC++;
}
}
return list.get(n - 1);
}
}
进行优化
1 不需要先判断是否存在相等值,再加入list
2 也无需使用list,长度n已知
class Solution {
public int nthUglyNumber(int n) {
// 新的丑数都是由2/3/5再乘一个小丑数构成;即后面的结果与前面有关
// 创建备忘录
int[] dp = new int[n];
// 备忘录初始化
dp[0] = 1;
// 向前步进
int i2 = 0, i3 = 0, i5 = 0;
for(int i = 1; i < n; i++){
dp[i] = Math.min(Math.min(2*dp[i2], 3*dp[i3]), 5*dp[i5]);
if(2*dp[i2] == dp[i]) i2++;
if(3*dp[i3] == dp[i]) i3++;
if(5*dp[i5] == dp[i]) i5++;
}
return dp[n - 1];
}
}
2.2 方法二(优先队列)
应该还能优化,queue里存的太多了,不止n-1个丑数
class Solution {
public int nthUglyNumber(int n) {
// HashSet和PriorityQueue同步更新,set用于查重,queue用于得到当前队列最大值
HashSet<Long> set = new HashSet<>();
PriorityQueue<Long> queue = new PriorityQueue<>();
int[] ans = new int[n];
int[] select = {2, 3, 5};
ans[0] = 1; set.add(1L); queue.add(1L);
for(int i = 0; i < n; i ++){
long oldUgly = queue.poll();
ans[i] = (int)(oldUgly);
for(int k: select){
if(!set.contains(k * oldUgly)){
set.add(k * oldUgly);
queue.add(k * oldUgly);
}
}
}
return ans[n - 1];
}
}