题目:
我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。
思路:
看到网上有两种思路,一种是最小堆的思想,一种是三指针,类似于动态规划的思想,下面针对三指针动态规划详细说明一下。
三指针动态规划的思想其实就是类似于合并两个有序数组的思想。
记丑数数组为ugly;那么ugly 的排列一定是{1,2,3,4,5,6,8……}。
根据丑数的定义,任意一个丑数都是由小于它的某一个丑数×因子(2或3或5)得到的。假设当前的丑数是ugly[i],如何得到有序数组的下一个丑数元素呢?
现在假设有3个数组,分别是
A:{1×2,2×2,3×2,4×2,……}
B:{1×3,2×3,3×3,4×3,……}
C:{1×5,2×5,3×5,4×5,……}
观察上面数组的特点,都是由较小的丑数乘以不同的丑数因子得到的丑数数组,那么所有的整数排列就是这三个数组的不断的动态去重、合并排序得到的。
假设三个数组的指针分别是p2,p3,p5. 然后比较A[p2],B[p2],C[p5],得到最小的数ugly[1] = 2,那么这时候ugly数组就是{1,2}了。p2指针应向右挪一个。为了去重,如果多个元素都是最小,那么这多个指针都要往后移动一个。
代码如下:
class Solution {
public:
int nthUglyNumber(int n) {
//丑数是除了从7开始的,质数 以及他们的倍数。
//三指针法,类似于合并两个有序数组,这里是合并
if(n==1) return 1;
int p2 = 0,p3=0,p5=0;
vector<int> ugly(n,0);
ugly[0] =1;
for(int i=1;i<n;i++){
int num2 = 2*ugly[p2], num3 = 3*ugly[p3], num5 = 5*ugly[p5];
ugly[i] = min(min(num2,num3),num5);
if(ugly[i]==num2) p2++;
if(ugly[i]==num3) p3++;
if(ugly[i]==num5) p5++;
}
return ugly[n-1];
}
};
第二种方法:最小堆
利用无序数组unordered_set去重。
初始时,堆为空,将1压入堆中,假如x是丑数,那么2x,3x,5x都是丑数 ,所以将他们压入堆中,执行n次就能找到第n大的丑数,只是这样浪费的空间会更多一点。
class Solution {
public:
int nthUglyNumber(int n) {
vector<int> factors={2,3,5};
unordered_set<long> seen;
priority_queue<long,vector<long>,greater<long>> pq;
seen.insert(1L);
pq.push(1L);
int ugly = 0;
for(int i=0;i<n;i++){
long cur = pq.top();
ugly = (int)cur;
pq.pop();
for(auto factor: factors){
int next = cur*factor;
if(!seen.count(next)){
seen.insert(next);
pq.push(next);
}
}
}
return ugly;
}
};