Leetcode 丑数

LC 264. 丑数 II

题目:你一个整数 n ,请你找出并返回第 n 个 丑数 。丑数 就是只包含质因数 2、3 和/或 5 的正整数。
方法:介绍三种方法,从最直观的开始

方法一:堆+欧拉筛

每次从堆中取一个最小的,然后分别乘2,3,5进行扩展。每次取出的最小值加入答案,需要去重。
有大量的重复,需要欧拉筛优化才能过

class Solution {
public:
    typedef long long ll;
    int nthUglyNumber(int n) {
        vector<ll>primes = {2, 3, 5};
        priority_queue<ll, vector<ll>, greater<ll>>pq;
        pq.push(1);
        set<ll>res;
        while(res.size() < n) {
            ll p = pq.top();pq.pop();
            res.insert(p);
            for(int prime : primes) {
                pq.push(prime * p);
                if(p % prime == 0)  break;  // 必需的优化,欧拉筛,p是由prime扩展来的
            }
        }
        return *(--res.end());  // 集合中的最后一个
    }
};

方法二:二分

考虑第i个元素是如何生成的,肯定是从已经生成的元素中找到一个值,其乘以2/3/5的值大于且最接近第i-1个。
对于2,我们可以从第一个开始枚举,直到超过第i-1元素,3、5同理;
然后取三者的最小值
由于已生成的数组是有序的,因此可以二分查找

class Solution {
public:
    int nthUglyNumber(int n) {
        vector<int>dp;
        dp.push_back(1);
        for(int i = 1;i < n;i++) {
            int a = *upper_bound(dp.begin(), dp.end(), dp[dp.size()-1]/2) * 2;
            int b = *upper_bound(dp.begin(), dp.end(), dp[dp.size()-1]/3) * 3;
            int c = *upper_bound(dp.begin(), dp.end(), dp[dp.size()-1]/5) * 5;
            dp.push_back(min(a, min(b, c)));
        }
        return dp[n-1];
    }
};

方法三:三指针

我们从方法二中得到启发,可以发现,二分查找到的点总是往右移的,且如果未采用这个点的值说明该点还未被使用,点不用右移;使用的就右移一位。

class Solution {
public:
    int nthUglyNumber(int n) {
        int i2 = 0, i3 = 0, i5 = 0;
        vector<int>dp(n, 0);
        dp[0] = 1;
        for(int i = 1;i < n;i++) {
            dp[i] = min(dp[i2]*2, min(dp[i3]*3, dp[i5]*5));
            if(dp[i] == dp[i2]*2)  i2++;
            if(dp[i] == dp[i3]*3) i3++;  // 不能用else if,为了去重
            if(dp[i] == dp[i5]*5) i5++;
        }
        return dp[n-1];
    }
};

LC 313. 超级丑数

题目:与上题相比,不只2,3,4,而是由一个primes数组

方法:堆+欧拉筛

class Solution {
public:
    typedef long long ll;
    int nthSuperUglyNumber(int n, vector<int>& primes) {
        priority_queue<ll, vector<ll>, greater<ll>>pq;
        pq.push(1);
        set<ll>res;
        while(res.size() < n) {
            ll p = pq.top();pq.pop();
            res.insert(p);
            for(int prime : primes) {
                pq.push(prime * p);
                if(p % prime == 0)  break;  // 欧拉筛,p是由prime扩展来的
            }
        }

        return *(--res.end());  // 集合中的最后一个
    }
};

当然,也可以使用上面两种方法。

posted @ 2022-01-12 14:34  Rogn  阅读(53)  评论(0编辑  收藏  举报