丑数(系列)
263. 丑数
丑数 就是只包含质因数 2、3 和 5 的正整数。
给你一个整数 n ,请你判断 n 是否为 丑数 。如果是,返回 true ;否则,返回 false 。
示例 1:
输入:n = 6 输出:true 解释:6 = 2 × 3 示例 2:
输入:n = 1 输出:true 解释:1 没有质因数,因此它的全部质因数是 {2, 3, 5} 的空集。习惯上将其视作第一个丑数。 示例 3:
输入:n = 14 输出:false 解释:14 不是丑数,因为它包含了另外一个质因数 7 。
分别对n反复除以质因子2,3,5, 如果最后剩余的值为1,则OK
class Solution {
public:
bool isUgly(int n) {
if (n <= 0) return false;
if (n == 1) return true;
int factors[3] = {2, 3, 5};
for (auto factor : factors) {
while (n % factor == 0) n = n/factor;
}
return n == 1;
}
};
264. 丑数 II
给你一个整数 n ,请你找出并返回第 n 个 丑数 。
丑数 就是只包含质因数 2、3 和/或 5 的正整数。
示例 1:
输入:n = 10 输出:12 解释:[1, 2, 3, 4, 5, 6, 8, 9, 10, 12] 是由前 10 个丑数组成的序列。 示例 2:
输入:n = 1 输出:1 解释:1 通常被视为丑数。
暴力法
直接按自然数顺序遍历isUgly, 但是时间复杂度O(nlogn)太高,
class Solution {
public:
int nthUglyNumber(int n) {
if (n == 1) return 1;
int count = 1;
int i = 1;
while (count < n) {
i++;
if (isUgly(i)) {
count++;
}
}
return i;
}
bool isUgly(int n) {
if (n <= 0) return false;
if (n == 1) return true;
int factors[3] = {2, 3, 5};
for (auto factor : factors) {
while (n % factor == 0) n = n/factor;
}
return n == 1;
}
};
小顶堆
每次从堆顶取出最小值,然后乘以三个因子后的数加入堆,为了避免重复,可以使用集合过滤。
时间复杂度也是O(nlogn), 但是确能通过OJ了
class Solution {
public:
int nthUglyNumber(int n) {
int factors[3] = {2, 3, 5};
unordered_set<long> seen;
// 默认大顶堆
priority_queue<long, vector<long>, greater<long>> heap;
long ans = 1;
heap.push(ans);
seen.insert(ans);
long cur = 1;
for(int i = 0; i < n; i++) {
cur = heap.top();
heap.pop();
for (auto factor : factors) {
long next = cur * factor;
if (!seen.count(next)) {
seen.insert(next);
heap.push(next);
}
}
}
return cur;
}
};
三个数组(或者叫动态规划)
dp[i] = min(dp[i-1]*2, dp[i-1]*3, dp[i-1]*5) 初始时候dp[1] = 1; 后面不断的乘以2,3,5分别可以得到3个数组,就是不知道下一次该选谁,所以有三个指针记录当前走到的位置。
class Solution {
public:
int nthUglyNumber(int n) {
int factors[3] = {2, 3, 5};
vector<int> dp(n+1);
int p2 = 1, p3 = 1, p5 = 1;
dp[1] = 1;
int pre = 1, ans = 1;
for (int i = 2; i <= n; i++) {
int num2 = dp[p2]*2, num3 = dp[p3]*3, num5 = dp[p5]*5;
dp[i] = min(num2, min(num3, num5));
if (dp[i] == num2) p2++;
if (dp[i] == num3) p3++;
if (dp[i] == num5) p5++;
}
return dp[n];
}
};
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 <= 105 1 <= primes.length <= 100 2 <= primes[i] <= 1000 题目数据 保证 primes[i] 是一个质数 primes 中的所有值都 互不相同 ,且按 递增顺序 排列
思路和普通丑数的处理方式一样
class Solution {
public:
int nthSuperUglyNumber(int n, vector<int>& primes) {
int prime_size = primes.size();
vector<int> pointers(prime_size, 1); //指针列表
vector<long> tmp_nums(prime_size, 1); //存储每次计算的临时结果
vector<long> dp(n+1, INT_MAX);
dp[1] = 1;
for (int i = 2; i <=n; i++) {
for (int p_index = 0; p_index < prime_size; p_index++) {
tmp_nums[p_index] = primes[p_index]*dp[pointers[p_index]];
if (tmp_nums[p_index] < dp[i]) dp[i] = tmp_nums[p_index];
}
for (int p_index = 0; p_index < prime_size; p_index++) {
if (dp[i] == tmp_nums[p_index]) {
pointers[p_index]++;
}
}
}
return dp[n];
}
};
1201. 丑数 III
给你四个整数:n 、a 、b 、c ,请你设计一个算法来找出第 n 个丑数。
丑数是可以被 a 或 b 或 c 整除的 正整数 。
示例 1:
输入:n = 3, a = 2, b = 3, c = 5 输出:4 解释:丑数序列为 2, 3, 4, 5, 6, 8, 9, 10… 其中第 3 个是 4。 示例 2:
输入:n = 4, a = 2, b = 3, c = 4 输出:6 解释:丑数序列为 2, 3, 4, 6, 8, 9, 10, 12… 其中第 4 个是 6。 示例 3:
输入:n = 5, a = 2, b = 11, c = 13 输出:10 解释:丑数序列为 2, 4, 6, 8, 10, 11, 12, 13… 其中第 5 个是 10。 示例 4:
输入:n = 1000000000, a = 2, b = 217983653, c = 336916467 输出:1999999984
这题和前面的题,似乎都不太一样,
递推的思路,时间复杂度太高
class Solution {
public:
int nthUglyNumber(int n, int a, int b, int c) {
int factors[3] = {a, b, c};
vector<int> dp(n+2, INT_MAX);
int p2 = 1, p3 = 1, p5 = 1;
dp[1] = 1;
int pre = 1, ans = 1;
for (int i = 2; i <= n+1; i++) {
// 这一行的计算和以前不一样
int num2 = p2*factors[0], num3 = p3*factors[1], num5 = p5*factors[2];
// cout << "p2,p3,p5: " << p2 << " " << p3 << " " << p5 << endl;
// cout << "num2,num3,num5: " << num2 << "," << num3 << ',' << num5;
dp[i] = min(num2, min(num3, num5));
if (dp[i] == num2) p2++;
if (dp[i] == num3) p3++;
if (dp[i] == num5) p5++;
cout << " " << dp[i];
}
return dp[n+1];
}
};
二分
TODO