剑指offer 丑数(dp、指针)

nowcoder 题目链接
leetcode 题目链接

描述

把只包含质因子 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 0n2000
要求:空间复杂度 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 223 2 ∗ 3 ∗ 2 2*3*2 232 3 ∗ 2 ∗ 2 3*2*2 322 出现三次。可以做个规定,质因子一旦存在 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 223 的顺序出现一次。

之后的想法是:维护 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 23 3 ∗ 2 3*2 32。在上面的代码中,会同时触发这两行:

	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 !

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

m0_51864047

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值