【力扣】丑数

丑数

1. 概述

  • 根据百度百科的解释,丑数有两种定义:

定义一

  • 把只包含质因子2,3和5的数称作丑数(Ugly Number)。

定义二

  • 给定一个素数集合: S = { p 1 , p 2 , . . . , p k } S = \{ p_1, p_2, ..., p_k \} S={p1,p2,...,pk},如果一个数x的质因子全部在集合S中,则x被称作丑数。

2. 例题

Leetcode 0263 丑数

题目描述:Leetcode 0263 丑数

在这里插入图片描述

分析

  • 本题的考点:数学

  • n中所有因子2、3、5全部除干净,最后判断n是否等于1即可。

代码

  • C++
class Solution {
public:
    bool isUgly(int n) {
        if (n <= 0) return false;
        while (n % 2 == 0) n /= 2;
        while (n % 3 == 0) n /= 3;
        while (n % 5 == 0) n /= 5;
        return n == 1;
    }
};
  • Java
class Solution {
    public boolean isUgly(int num) {
        if (num <= 0) return false;
        while (num % 2 == 0) num /= 2;
        while (num % 3 == 0) num /= 3;
        while (num % 5 == 0) num /= 5;
        return num == 1;
    }
}

时空复杂度分析

  • 时间复杂度: O ( l o g ( n ) ) O(log(n)) O(log(n))

  • 空间复杂度: O ( 1 ) O(1) O(1)

Leetcode 0264 丑数 II

题目描述:Leetcode 0264 丑数 II

在这里插入图片描述

分析

  • 本题的考点:归并排序

  • 本题的实质是三路归并

  • 设丑数的集合为S,则S可以分为四个集合的并集,即 S = { 1 } ∪ S 2 ∪ S 3 ∪ S 5 S = \{1\} \cup S_2 \cup S_3 \cup S_5 S={1}S2S3S5,其中 S 2 S_2 S2表示是2的倍数的所有丑数的集合。

  • 我们可以发现 S 2 = 2 × S S_2 = 2 \times S S2=2×S S 3 = 3 × S S_3 = 3 \times S S3=3×S S 5 = 5 × S S_5 = 5 \times S S5=5×S

  • 因此我们可以让S中初始有一个元素1,然后让三个指针分别指向1,然后分别乘以2、3、5可以得到 S 2 、 S 3 、 S 5 S_2、S_3、S_5 S2S3S5中的第一个数,放入S中,最终就可以得到S

  • 本题的整体过程:由S可以构造 S 2 、 S 3 、 S 5 S_2、S_3、S_5 S2S3S5,然后由 S 2 、 S 3 、 S 5 S_2、S_3、S_5 S2S3S5又可以构造S

代码

  • C++
class Solution {
public:
    int nthUglyNumber(int n) {

        vector<int> q(1, 1);
        for (int i = 0, j = 0, k = 0; q.size() < n;) {
            int t = min(q[i] * 2, min(q[j] * 3, q[k] * 5));
            q.push_back(t);
            if (q[i] * 2 == t) i++;
            if (q[j] * 3 == t) j++;
            if (q[k] * 5 == t) k++;
        }
        return q.back();
    }
};
  • Java
class Solution {
    
    public int nthUglyNumber(int n) {

        List<Integer> q = new ArrayList<>();
        q.add(1);
        for (int i = 0, j = 0, k = 0; q.size() < n; ) {
            int t = Math.min(q.get(i) * 2, Math.min(q.get(j) * 3, q.get(k) * 5));
            q.add(t);
            if (q.get(i) * 2 == t) i++;
            if (q.get(j) * 3 == t) j++;
            if (q.get(k) * 5 == t) k++;
        }
        return q.get(q.size() - 1);
    }
}

时空复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n)

  • 空间复杂度: O ( n ) O(n) O(n)

Leetcode 0313 超级丑数

题目描述:Leetcode 0313 超级丑数

在这里插入图片描述

分析

  • 本题的考点:多路归并

  • 本题和Leetcode 0264 丑数 II很类似,区别在于本题不一定是三路归并,做法类似。

  • 这里使用小顶堆记录多个序列得到的数据,每次从堆顶取出一个数据(设该数据所在的序列为T),当这个数据不和生成的丑数序列(设为S)中的数据重复时,放入丑数序列。之后还需要生成该序列T中的下一个数据,将取出的数据的生成下一个丑数放入到堆中。

  • 为了记录方便,这里堆中存放的是一个pair(first, seond)表示:(某个序列中的元素,该元素是序列中的第几个元素)。元素从0开始编号。

代码

  • C++
class Solution {
public:
    int nthSuperUglyNumber(int n, vector<int>& primes) {

        // (指针对应的数 * 该指针指向的质数,指针下标)
        // 比如:初始,指针都指向0,q[0] = 1, (q[0]*x, 0)
        typedef pair<int, int> PII;
        priority_queue<PII, vector<PII>, greater<PII>> heap;  // 小顶堆
        // 刚开始primes.size()个指针下标都指向0
        for (int x : primes) heap.push({x, 0});

        vector<int> q(n);
        q[0] = 1;
        for (int i = 1; i < n;) {
            auto t = heap.top(); heap.pop();
            if (t.first != q[i - 1]) q[i++] = t.first;
            int idx = t.second, p = t.first / q[idx];  // 该序列是Sp
            heap.push({p * q[idx + 1], idx + 1});
        }
        return q[n - 1];
    }
};
  • Java
class Solution {

    // (指针对应的数 * 该指针指向的质数,指针下标)
    //  比如:初始,指针都指向0,q[0] = 1, (q[0]*x, 0)
    static class MyPair implements Comparable<MyPair> {
        int x, y;

        public MyPair(int x, int y) {
            this.x = x;
            this.y = y;
        }

        @Override
        public int compareTo(MyPair o) {
            return this.x - o.x;
        }
    }

    public int nthSuperUglyNumber(int n, int[] primes) {

        Queue<MyPair> pq = new PriorityQueue<>();  // 默认小顶堆
        // 刚开始primes.size()个指针下标都指向0
        for (int x : primes) pq.add(new MyPair(x, 0));

        int[] q = new int[n];
        q[0] = 1;
        for (int i = 1; i < n; ) {
            MyPair t = pq.remove();
            if (t.x != q[i - 1]) q[i++] = t.x;
            int idx = t.y, p = t.x / q[idx];
            pq.add(new MyPair(p * q[idx + 1], idx + 1));
        }
        return q[n - 1];
    }
}

时空复杂度分析

  • 时间复杂度: O ( n log ⁡ ( n ) ) O(n \log(n)) O(nlog(n))

  • 空间复杂度: O ( n ) O(n) O(n)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值