• 剑指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 个丑数。
    
    • 1
    • 2
    • 3

    思路一

    直接三叉树暴搜复杂度必然是指数级别,首先想到一个小优化:例如 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()});
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    注意:牛客网提交时,特判第 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];
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    可以关注一些小细节它是如何解决的,比如我前面提到的那种情况: 2 ∗ 3 2*3 23 3 ∗ 2 3*2 32。在上面的代码中,会同时触发这两行:

    	if(vec[i]==vec[i2]*2) i2++;
    	if(vec[i]==vec[i3]*3) i3++;
    
    • 1
    • 2

    但并不影响它的 O ( 1 ) O(1) O(1) 复杂度。

    其他

    今天看别人代码,发现一个新奇的东西:

    int main(){
    	//...
    	return 0 ^ 0;
    }
    
    • 1
    • 2
    • 3
    • 4

    ! 0 !

  • 相关阅读:
    国产大语言模型ChatGLM3本地搭建、使用和功能扩展
    LVS DR模式负载均衡群集部署
    【后端】黑马MVC案例详解
    mysql8离线安装
    单样本T检验|独立样本T检验|配对样本T检验(绘图)
    Protobuf:Updating A Message (Protobuf更新.proto文件后用读取旧的信息流)
    『Java』初试JavaAgent实现修改字节码和插桩
    C#Assembly的使用
    高项 06 项目进度管理
    【工作总结】证书到底有什么用?
  • 原文地址:https://blog.csdn.net/m0_51864047/article/details/126695889