首页 > 技术文章 > 【LeetCode-堆】丑数

flix 2020-05-25 22:47 原文

题目描述

编写一个程序,找出第 n 个丑数。
丑数就是质因数只包含 2, 3, 5 的正整数。
示例:

输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。

说明:

  • 1 是丑数。
  • n 不超过1690。

题目链接: https://leetcode-cn.com/problems/ugly-number-ii/

思路1

使用小根堆来做。丑数就是质因数只包含 2, 3, 5 的正整数,1 是丑数。所以我们先将 1 放入堆中,然后取出当前的堆顶元素 ans,将 ans*2、ans*3、ans*5 依次放入堆中。这样,我们第 i 循环得到的堆顶元素就是第 i 个丑数。因为生成丑数的过程可能出现重复的情况,例如 4*3 = 2*6,所以在将 ans*2、ans*3、ans*5 依次放入堆之前,还要判断这 3 个值是否在堆中出现过,如果没有出现过,则放入堆中,否则不放。可以使用 set 或者哈希表来记录元素是否出现过。代码如下:

class Solution {
public:
    int nthUglyNumber(int n) {
        priority_queue<long, vector<long>, greater<long>> q;  // 要使用 long
        set<long> s;  // 要使用 long
        s.insert(1);
        q.push(1);
        long ans = 1;
        for(int i=1; i<=n; i++){
            if(s.count(ans*2)==0){
                q.push(ans*2);
                s.insert(ans*2);
            }
            if(s.count(ans*3)==0){
                q.push(ans*3);
                s.insert(ans*3);
            }
            if(s.count(ans*5)==0){
                q.push(ans*5);
                s.insert(ans*5);
            }
            ans = q.top(); q.pop();
        }
        return ans;
    }
};
  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(n)

思路2

使用动态规划来做。使用 dp[i] 表示第 i+1 个丑数(i 从 0 开始),例如 dp[0] 表示第 1 个丑数。使用三个指针 p2, p3, p5,状态转移方程为 dp[i] = min(2*dp[p2], 3*dp[p3], 5*dp[p5]),i > 0,也就是 dp[i] 是 2*dp[p2]、3*dp[p3] 和 5*dp[p5] 之间的最小值。如果最小值是 2*dp[p2],则 p2++;如果最小值是 3*dp[p3],则 p3++;如果最小值是 5*dp[p5],则 p5++。代码如下:

class Solution {
public:
    int nthUglyNumber(int n) {
        vector<long> dp(n);
        dp[0] = 1;
        int p2 = 0;
        int p3 = 0;
        int p5 = 0;
        for(int i=1; i<n; i++){
            dp[i] = min(2*dp[p2], min(3*dp[p3], 5*dp[p5]));
            if(dp[i]==2*dp[p2]) p2++;
            if(dp[i]==3*dp[p3]) p3++;  // 不是 else if
            if(dp[i]==5*dp[p5]) p5++;  // 不是 else if
        }
        return dp[n-1];
    }
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

推荐阅读