Leetcode--Java--313. 超级丑数

题目描述

超级丑数 是一个正整数,并满足其所有质因数都出现在质数数组 primes 中。

给你一个整数 n 和一个整数数组 primes ,返回第 n 个 超级丑数 。

题目数据保证第 n 个 超级丑数 在 32-bit 带符号整数范围内。

样例描述

示例 1:

输入:n = 12, primes = [2,7,13,19]
输出:32 
解释:给定长度为 4 的质数数组 primes = [2,7,13,19],前 12 个超级丑数序列为:[1,2,4,7,8,13,14,16,19,26,28,32] 。
示例 2:

输入:n = 1, primes = [2,3,5]
输出:1
解释:1 不含质因数,因此它的所有质因数都在质数数组 primes = [2,3,5] 中。

思路

本体整体思路与下面这题类似,
Leetcode–Java–264. 丑数 II
关键:丑数乘以质因数还是丑数,上题只有三个质因数,这题有k个
方法一:

  1. 优先队列(最小堆)
  2. 维护所有含有质因数的丑数,不断放入优先队列。然后每次从队列中取出最小的丑数(质因数),乘以数组中的所有质因数,再次放入,连续n次操作就可以得到第
    n个了。

方法二:多路归并 + 动态规划

  1. 维护一个三元组,存储信息如下:
    在这里插入图片描述
  2. 这题属于k路归并,维护k个指针,但不需要构造这k个序列。可以每次取出k个指针中最小的那个,然后将该指针往后移动(即将当前序列的下一个值放入堆中)
  3. 初始化为 (primes[i], i, 0)加入优先队列中,每次从堆中取出最小元素,那么下一个该放入的元素为 (ans[idx + 1] * primes[i], i, idx + 1)
  4. 去重处理。不需要用Set,因为k个指针指向的序列,以及ans结果都是单调递增的,可以直接比较当前数与ans的最后一位进行比较来实现去重。

代码

方法一:优先队列

class Solution {
    public int nthSuperUglyNumber(int n, int[] primes) {
           long ugly = -1;
           Set<Long> seen = new HashSet<>();
           //本身就是升序,也就是小顶堆
           PriorityQueue<Long> pq = new PriorityQueue<>();
           pq.offer(1L);
           seen.add(1L);
           for (int i = 0; i < n; i ++ ) {
               long curNum = pq.poll();
               ugly = curNum;
              for (long prime: primes) {
                  //丑数乘以质因数还是丑数
                  long x = curNum * prime;
                  //不存在才加入
                  if (!seen.contains(x)) {
                      seen.add(x);
                      pq.offer(x);
                  }
              }
           }
           return (int)ugly;
    }
}

方法二:动态规划 + 优先队列 + 多路归并

class Solution {
    public int nthSuperUglyNumber(int n, int[] primes) {
        int m = primes.length;
        int ans[] = new int[n];
        ans[0] = 1;
        //按丑数升序的最小堆  
        PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[0] - b[0]);
       //先初始化这k个序列
       for (int i = 0; i < m; i ++ ) {
           pq.offer(new int[]{primes[i], i, 0});
       }
         int cnt = 1;
         //直到结果满了才退出
         while (cnt < n) {
 //拿到当前k个指针里面最小的数的相关信息: 1.值 2.所在序列下标 3.当前丑数结果集下标
             int num[] = pq.poll();
             int val = num[0], i = num[1], idx = num[2];
             //不是重复才加到结果集
             if (ans[cnt - 1] != val) {
                ans[cnt] = val;
                cnt ++;
             }
             //加入primes[i]为质因数所在序列的下一个数
             pq.offer(new int[]{primes[i] * ans[idx + 1], i, idx + 1});
         }
         return ans[n - 1];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值