描述
把只包含质因子 2 2 2、 3 3 3 和 5 5 5 的数称作丑数(Ugly Number)。例如 6 6 6、 8 8 8 都是丑数,但 14 14 14 不是,因为它包含质因子 7 7 7。 习惯上我们把 1 1 1 当做是第一个丑数。求按从小到大的顺序的第 n n n 个丑数。
数据范围:
0
≤
n
≤
2000
0 \le n \le 2000
0≤n≤2000
要求:空间复杂度
O
(
n
)
O(n)
O(n) , 时间复杂度
O
(
n
)
O(n)
O(n)
输入:7
返回值:8
//解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。
思路一
直接三叉树暴搜复杂度必然是指数级别,首先想到一个小优化:例如 12 12 12,在三叉搜索树中会以 2 ∗ 2 ∗ 3 2*2*3 2∗2∗3、 2 ∗ 3 ∗ 2 2*3*2 2∗3∗2、 3 ∗ 2 ∗ 2 3*2*2 3∗2∗2 出现三次。可以做个规定,质因子一旦存在 3 3 3,那么之后只能 × 3 \times 3 ×3 或 × 5 \times 5 ×5;质因子一旦存在 5 5 5,那么之后只能 × 5 \times 5 ×5。这样 12 12 12 就只会以 2 ∗ 2 ∗ 3 2*2*3 2∗2∗3 的顺序出现一次。
之后的想法是:维护 3 3 3 个递增队列,三个队首元素中最小的就是下一个丑数。每 pop \text{pop} pop 一个丑数后,在其他队列中 push \text{push} push 候选数字。( push \text{push} push 时注意之前的规定)
时空复杂度均为 O ( n ) O(n) O(n).
class Solution {
public:
int GetUglyNumber_Solution(int index) {
if(index<=1) return index;
queue<int> q2,q3,q5;
q2.push(2);
q3.push(3);
q5.push(5);
for(int i=1;i<index-1;i++){
int mn=min({q2.front(),q3.front(),q5.front()});
if(mn==q2.front()) q2.push(mn*2),q3.push(mn*3),q5.push(mn*5),q2.pop();
if(mn==q3.front()) q3.push(mn*3),q5.push(mn*5),q3.pop();
if(mn==q5.front()) q5.push(mn*5),q5.pop();
}
return min({q2.front(),q3.front(),q5.front()});
}
};
注意: 在牛客网提交时,特判第 0 0 0 个丑数为 0 0 0。
思路二
大概意思是,每个丑数 × 2 \times2 ×2、 × 3 \times3 ×3、 × 5 \times5 ×5 后,都会产生更大的丑数。然后维护这三个并行、互不影响的维度,每次取最小的一个,并且更新它。
class Solution {
public:
int GetUglyNumber_Solution(int index) {
vector<int> vec(index+10,0);
int i2=1,i3=1,i5=1;
vec[1]=1;
for(int i=2;i<=index;i++){
vec[i]=min({vec[i2]*2,vec[i3]*3,vec[i5]*5});
if(vec[i]==vec[i2]*2) i2++;
if(vec[i]==vec[i3]*3) i3++;
if(vec[i]==vec[i5]*5) i5++;
}
return vec[index];
}
};
可以关注一些小细节它是如何解决的,比如我前面提到的那种情况: 2 ∗ 3 2*3 2∗3 和 3 ∗ 2 3*2 3∗2。在上面的代码中,会同时触发这两行:
if(vec[i]==vec[i2]*2) i2++;
if(vec[i]==vec[i3]*3) i3++;
但并不影响它的 O ( 1 ) O(1) O(1) 复杂度。
其他
今天看别人代码,发现一个新奇的东西:
int main(){
//...
return 0 ^ 0;
}
! 0 !