264. Ugly Number II
仅供自己学习
思路:
暴力
刚开始看确实没思路,用的暴力法。因为他要的数是质因数只为2,3,5的。那么我们只需要获得通过2,3,5相乘得到的数即可。通过三个循环,先a=a2,然后b=a,b=b3,最后c=b,c=c5,这样得到的c就是是由23*5得到的数,但是这样得到的数,没法确定大小关系,所以还需要一个数组存入排序后,才能获得第n个丑数
a,b,c得用longlong,不然会溢出
class Solution {
public:
int nthUglyNumber(int n) {
vector<int> res;
for(long long a = 1;a<=INT_MAX;a=a*2){
for(long long b=a;b<=INT_MAX;b=b*3){
for(long long c=b;c<=INT_MAX;c=c*5){
res.push_back(c);
}
}
}
sort(res.begin(),res.end());
return res[n-1]
}
};
小顶堆
因为我们需要升序第n个数,而且暴力做法有个问题就是没法确定大小关系,那么我们通过堆的数据结构,每次都能保证取到当前最小的数来获取新的较小的丑数。那么我们只需要循环n-1次就可以得到这个丑数,因为1默认为丑数,所以用n-1。但是这里还有个问题就是重复的问题,对于12,有2和6,4和3 两种情况,而这两种都是满足丑数,所以我们还需要把重复的元素删掉,因为我们每次都会获取堆顶元素,并弹出,如果有重复的那么堆顶的元素会等于取出来的元素,通过这个条件可以去掉相同元素。
代码:
class Solution {
public:
int nthUglyNumber(int n) {
priority_queue <double,vector<double>,greater<double>> pq;
double answer=1;
for(int i=1;i<n;i++){
pq.push(answer*2);
pq.push(answer*3);
pq.push(answer*5);
answer=pq.top();
pq.pop();
while(!pq.empty()&&answer==pq.top()) pq.pop();
}
return answer;
}
};
因为set数据结构能也能自动排序且能自动去重,那么可以直接用set存放即可。
class Solution {
public:
int nthUglyNumber(int n) {
set<double> st;
double answer=1;
for(int i=1;i<n;i++){
st.insert(answer*2);
st.insert(answer*3);
st.insert(answer*5);
answer=*st.begin();
st.erase(answer);
}
return answer;
}
};
动态规划(三指针)
我们定义三个指针p2,p3,p4,并初始化为0,我们每次判断2dp[p2],3dp[p3],5dp[p5]谁更小,并将最小的赋给dp[i]相当于我们每次都获取最小的并将最小的值放在数组最前面。和堆不同的地方是,堆先放入数组在排序,动态规划是先排序再放入数组。然后再将赋给dp[i]的指针右移,因为现在的做法会让数组每个数都有2,3,5的数满足丑数,所以直接右移即可。
那么我们的放入数组的就是从小到大的排序,直接取第n-1个即可。
代码:
class Solution {
public:
int nthUglyNumber(int n) {
int p2=0,p3=0,p5=0;
vector<int> dp(n);
dp[0]=1;
for(int i=1;i<n;++i){
dp[i] = min(min(2*dp[p2],3*dp[p3]),5*dp[p5]);
if(dp[i]==2*dp[p2]) p2++;
if(dp[i]==3*dp[p3]) p3++;
if(dp[i]==5*dp[p5]) p5++;
}
return dp[n-1];
}
};