剑指 Offer 49. 丑数
我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。
示例:
输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。
说明:
1 是丑数。
n 不超过1690。
核心:动态规划dp,三指针,就是3个指针代表3个2,3,5为乘数的丑数的dp下标
可以理解为三匹马跑得速度不一样,a马跑得最慢速度为2,b马跑得中速为3,c马跑得最快速度为5,而a,b,c下标的数则代表时间(dp下标,因为dp数组的值只能为2,3,5相乘组成的丑数,不能为其他数)
class Solution {
public int nthUglyNumber(int n) {
//abc分别代表2,3,5为乘数的下标
int a=0,b=0,c=0;
int[] dp=new int[n];
dp[0]=1;
int n2,n3,n5;
for (int i = 1; i < n; i++) {
//因为dp的值总是为2,3,5相乘组成的丑数,而abc三个指针的位置都不同,分别为n2,n3,n5
n2=dp[a]*2;
n3=dp[b]*3;
n5=dp[c]*5;
//取之中最小的数放入dp数组
dp[i]=Math.min(Math.min(n2,n3),n5);
//只有当dp[i]等于自己指针计算出的丑数才++,有可能重复,则一起++
//因为已经放入了dp数组,如dp[1]*3=2*3和dp[2]*2=3*2,都++变成dp[2]*3=3*3和dp[3]*2=4*2继续循环
if (dp[i]==n2){
a++;
}
if (dp[i]==n3){
b++;
}
if (dp[i]==n5){
c++;
}
}
return dp[n-1];
}
}
核心:小顶堆(暴力解法,效率低)
class Solution {
public int nthUglyNumber(int n) {
Queue<Long> queue = new PriorityQueue<>();
queue.offer(1L);
Long a=2L,b=3L,c=5L;
Long res = 0L;
for (int i = 0; i < n; i++) {
Long poll = queue.poll();
if (i==n-1){
res=poll;
break;
}
//只要不存在相同,就每个2,3,5和当前出队的数全都插入队列
//(因此可能溢出,需要long类型运算,而且会浪费大概二分之一空间)
if (!queue.contains(a*poll)) {
queue.offer(a*poll);
}
if (!queue.contains(b*poll)) {
queue.offer(b*poll);
}
if (!queue.contains(c*poll)) {
queue.offer(c*poll);
}
}
return res.intValue();
}
}