Medium
题目描述
超级丑数是一个正整数,其质因数都在数组 primes 中。
给你一个整数 n 和一个整数数组 primes,请你返回第 n 个超级丑数。
题目数据保证第 n 个超级丑数在 32 位有符号整数范围内。
示例 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 <= 10^51 <= primes.length <= 1002 <= primes[i] <= 1000primes[i]保证是一个质数primes中的所有值都 互不相同 ,且按 递增顺序 排列
解题思路
这道题是经典的丑数问题的扩展版本。我们需要生成一个序列,其中每个数都只包含给定质数集合中的因子。
核心思路:动态规划 + 多路归并
基本思想:每个超级丑数都是由之前的超级丑数乘以某个质数得到的。我们维护一个结果数组,从1开始逐步构建。
多指针法:为每个质数维护一个指针,指向当前应该与该质数相乘的超级丑数的位置。这样可以确保生成的序列是有序的。
避免重复:当多个质数产生相同的候选值时,需要同时移动对应的指针,避免重复添加。
算法步骤:
- 初始化结果数组,第一个元素为1
- 为每个质数创建一个指针,初始都指向索引0
- 每次选择所有候选值中的最小值作为下一个超级丑数
- 更新所有产生最小值的指针
时间复杂度优化:使用多指针法比暴力枚举或优先队列的方法更高效,时间复杂度为O(n×k),其中k是质数的个数。
代码实现
class Solution {
public:
int nthSuperUglyNumber(int n, vector<int>& primes) {
vector<int> ugly(n);
ugly[0] = 1;
vector<int> pointers(primes.size(), 0);
for (int i = 1; i < n; i++) {
int minVal = INT_MAX;
for (int j = 0; j < primes.size(); j++) {
minVal = min(minVal, ugly[pointers[j]] * primes[j]);
}
ugly[i] = minVal;
for (int j = 0; j < primes.size(); j++) {
if (ugly[pointers[j]] * primes[j] == minVal) {
pointers[j]++;
}
}
}
return ugly[n-1];
}
};
class Solution:
def nthSuperUglyNumber(self, n: int, primes: List[int]) -> int:
ugly = [1]
pointers = [0] * len(primes)
for i in range(1, n):
candidates = [ugly[pointers[j]] * primes[j] for j in range(len(primes))]
min_val = min(candidates)
ugly.append(min_val)
for j in range(len(primes)):
if candidates[j] == min_val:
pointers[j] += 1
return ugly[n-1]
public class Solution {
public int NthSuperUglyNumber(int n, int[] primes) {
int[] ugly = new int[n];
ugly[0] = 1;
int[] pointers = new int[primes.Length];
for (int i = 1; i < n; i++) {
int minVal = int.MaxValue;
for (int j = 0; j < primes.Length; j++) {
minVal = Math.Min(minVal, ugly[pointers[j]] * primes[j]);
}
ugly[i] = minVal;
for (int j = 0; j < primes.Length; j++) {
if (ugly[pointers[j]] * primes[j] == minVal) {
pointers[j]++;
}
}
}
return ugly[n-1];
}
}
/**
* @param {number} n
* @param {number[]} primes
* @return {number}
*/
var nthSuperUglyNumber = function(n, primes) {
const ugly = [1];
const indices = new Array(primes.length).fill(0);
for (let i = 1; i < n; i++) {
const candidates = primes.map((prime, idx) => prime * ugly[indices[idx]]);
const nextUgly = Math.min(...candidates);
ugly.push(nextUgly);
for (let j = 0; j < primes.length; j++) {
if (candidates[j] === nextUgly) {
indices[j]++;
}
}
}
return ugly[n - 1];
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n × k) | 其中 n 是要求的数字位置,k 是质数数组的长度,需要遍历 n-1 次,每次需要检查 k 个候选值 |
| 空间复杂度 | O(n + k) | 需要存储 n 个超级丑数的数组和 k 个指针数组 |
相关题目
- . Ugly Number II (Medium)