leetcode 264. 丑数 II
264. 丑数 II
暴力超时解🤡
class Solution {
public:
int nthUglyNumber(int n) {
if(n <= 6) return n;
n -= 6;
int num = 7;
while(n){
int temp = num;
while(temp % 2 == 0) temp /= 2;
while(temp % 3 == 0) temp /= 3;
while(temp % 5 == 0) temp /= 5;
if(temp == 1) --n;
++num;
}
return num-1;
}
};
直接看题解
官方题解:
法一:最小堆
class Solution {
public:
int nthUglyNumber(int n) {
// 定义一个包含质因数2、3、5的数组,这些质因数将用于生成丑数
vector<int> factors = {2, 3, 5};
// 创建一个集合,用于存储已经生成过的丑数,避免重复计算
unordered_set<long> seen;
// 创建一个优先队列(最小堆),用于存储和获取当前最小的丑数
priority_queue<long, vector<long>, greater<long>> heap;
// 将初始丑数1加入集合和优先队列中
seen.insert(1L);
heap.push(1L);
// 初始化丑数变量,用于存储最终结果
int ugly = 0;
// 循环n次,每次获取当前最小的丑数,并生成新的丑数
for (int i = 0; i < n; i++) {
// 获取当前优先队列中的最小值
long curr = heap.top();
heap.pop();
// 更新丑数变量为当前最小值
ugly = (int)curr;
// 遍历每个质因数,生成新的丑数
for (int &factor : factors) {
long next = curr * factor;
// 如果新生成的丑数未被访问过,则加入集合和优先队列
if (!seen.count(next)) {
seen.insert(next);
heap.push(next);
}
}
}
// 返回第n个丑数
return ugly;
}
};
法二:动态规划
class Solution {
public:
//dp[i]肯定是其前面的某个数*2或*3*或*5得出来的
//而dp[i]*2,dp[i]*3,dp[i]*5 这三个数,肯定也是dp 数组后面某个位置的数
//可以推断,dp这个数组上的数,每个位置肯定都要x2\x3\x5 一遍,其结果是放在dp 数组后面某个位置
//那就可以从这个数组初始的状态dp[1]=1 开始,用p2\p3\p5 表示当前该哪个位置该乘以2\3\5 了
//只要每次取乘以 2、3、5 后的结果中最小的值,那这个最小的值就是最新一个的dp 值,然后相应地移动一下计算出这个新dp 值的 p2(或 p3 或p5)索引,即该下一个数去乘以2(或3 或5)了。
//按次遍历,计算出第i个数,即为dp[i]
int nthUglyNumber(int n) {
// 创建一个大小为n+1的动态规划数组,用于存储前n个丑数
vector<int> dp(n + 1);
// 初始化第一个丑数为1
dp[1] = 1;
// 定义三个指针,分别对应质因数2、3、5的当前索引位置
int p2 = 1, p3 = 1, p5 = 1;
// 从第2个丑数开始,直到第n个丑数
for (int i = 2; i <= n; i++) {
// 计算当前指针指向的丑数分别乘以2、3、5的结果
int num2 = dp[p2] * 2, num3 = dp[p3] * 3, num5 = dp[p5] * 5;
// 当前新的丑数是这三个结果中的最小值
dp[i] = min(min(num2, num3), num5);
// 如果当前丑数是由num2得到的,则将p2指针后移一位
if (dp[i] == num2) p2++;
// 如果当前丑数是由num3得到的,则将p3指针后移一位
if (dp[i] == num3) p3++;
// 如果当前丑数是由num5得到的,则将p5指针后移一位
if (dp[i] == num5) p5++;
}
// 返回第n个丑数
return dp[n];
}
};