这是我参与8月更文挑战的第9天,活动详情查看:8月更文挑战
leetcode-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] 中。
提示:
- 1 <= n <= 106
- 1 <= primes.length <= 100
- 2 <= primes[i] <= 1000
- 题目数据 保证 primes[i] 是一个质数
- primes 中的所有值都 互不相同 ,且按 递增顺序 排列
Related Topics
- 数组
- 哈希表
- 数学
- 动态规划
- 堆(优先队列)
- 👍 188 👎 0
[题目链接]
[github地址]
[思路介绍]
思路一:优先队列+hash
- 扫描递增数组
- TreeSet + 优先队列还是很好考虑到的
- 但是如何维护递增属实是我没想到的
- 后来一想,原来如此简单
- 通过n计数
- set去重
- 优先队列维护小根堆
public int nthSuperUglyNumber(int n, int[] primes) {
Set<Long> set = new HashSet<>();
PriorityQueue<Long> priorityQueue = new PriorityQueue<>();
priorityQueue.add(1L);
set.add(1L);
while (n-- > 0) {
long val = priorityQueue.poll();
if (n == 0){
return (int)val;
}
for (int num: primes
) {
if (!set.contains(num*val)){
set.add(num*val);
priorityQueue.add(num*val);
}
}
}
return -1;
}
-
时间复杂度O(nm * lg(nm))
-
空间复杂度O(n*m)
思路二:多路归并
- 不难发现的是下一个丑数都是由之前的丑数*输入数组的质因子实现
- 主要是找到最近遍历的最小元素*最近遍历的最小输入因子
- 三叶大佬的三元组小根堆就很秀
public int nthSuperUglyNumber(int n, int[] primes) { int m = primes.length; PriorityQueue<int[]> q = new PriorityQueue<>((a, b) -> a[0] - b[0]); for (int i = 0; i < m; i++) { q.add(new int[]{primes[i], i, 0}); } int[] ans = new int[n]; //初始化第一个一定是1 ans[0] = 1; for (int j = 1; j < n;) { int[] temp = q.poll(); int val = temp[0], i = temp[1], idx = temp[2]; //维护单调递增 if (val != ans[j - 1]) ans[j++] = val; //这个步骤可以重复写ans数组保证丑数相邻关系 q.add(new int[]{ans[idx + 1] * primes[i], i, idx + 1}); } return ans[n - 1]; }
- 时间复杂度O(max{n*lgm,m})
- 空间复杂度O(n+m)