剑指 Offer 49. 丑数
思路:动态规划
设已知长度为n的丑数序列
x
1
,
x
2
,
x
3
,
⋯
,
x
n
x_1,x_2,x_3,\cdots,x_n
x1,x2,x3,⋯,xn,求第n+1个丑数,根据递推性质,丑数
x
n
+
1
x_{n+1}
xn+1只可能为:
f
(
x
)
=
{
x
a
×
2
a
∈
[
1
,
n
]
x
b
×
3
b
∈
[
1
,
n
]
x
c
×
5
c
∈
[
1
,
n
]
f(x)=\begin{cases} x_a\times2 & {a\in[1,n]}\\ x_b\times3 & {b\in[1,n]}\\ x_c\times5 & {c\in[1,n]} \end{cases}
f(x)=⎩⎪⎨⎪⎧xa×2xb×3xc×5a∈[1,n]b∈[1,n]c∈[1,n]
丑数递推公式为:
x n + 1 = m i n ( x a × 2 , x b × 3 , x c × 5 ) x_{n+1}=min(x_a\times2,x_b\times3,x_c\times5) xn+1=min(xa×2,xb×3,xc×5)
由于
x
n
+
1
x_{n+1}
xn+1是最接近
x
n
x_n
xn的丑数,因此索引a, b, c满足以下条件:
{
x
a
×
2
>
x
n
≥
x
a
−
1
×
2
即
x
a
为
首
个
乘
以
2
后
大
于
x
n
的
丑
数
x
b
×
3
>
x
n
≥
x
b
−
1
×
3
即
x
b
为
首
个
乘
以
3
后
大
于
x
n
的
丑
数
x
c
×
5
>
x
n
≥
x
c
−
1
×
5
即
x
c
为
首
个
乘
以
5
后
大
于
x
n
的
丑
数
\begin{cases} x_a\times2>x_n\ge x_{a-1}\times2 &即x_a为首个乘以2后大于x_n的丑数\\ x_b\times3>x_n\ge x_{b-1}\times3 &即x_b为首个乘以3后大于x_n的丑数\\ x_c\times5>x_n\ge x_{c-1}\times5 &即x_c为首个乘以5后大于x_n的丑数 \end{cases}
⎩⎪⎨⎪⎧xa×2>xn≥xa−1×2xb×3>xn≥xb−1×3xc×5>xn≥xc−1×5即xa为首个乘以2后大于xn的丑数即xb为首个乘以3后大于xn的丑数即xc为首个乘以5后大于xn的丑数
因此,设置指针a,b,c指向首个丑数,循环递推公式得到下个丑数,并每轮将对应指针+1
class Solution {
public:
int nthUglyNumber(int n) {
vector<int> dp(n);
dp[0]=1;
int a=0,b=0,c=0;
for(int i=1;i<n;++i){
int na=dp[a]*2;
int nb=dp[b]*3;
int nc=dp[c]*5;
dp[i]=min(na,min(nb,nc));
if(dp[i]==na)++a;
if(dp[i]==nb)++b;
if(dp[i]==nc)++c;
}
return dp[n-1];
}
};
时间复杂度 O(n)
空间复杂度 O(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>> heap;
seen.insert(1L);
heap.push(1L);
int ugly=0;
for(int i=0;i<n;++i){
long curr=heap.top();
heap.pop();
ugly=int(curr);
for(int factor:factors){
long next=curr*factor;
if(!seen.count(next)){
seen.insert(next);
heap.push(next);
}
}
}
return ugly;
}
};
时间复杂度 O(n*logn)
空间复杂度 O(n)