题目来源:LeetCode264:丑数 II
问题抽象: 给定整数 n,要求生成 第 n 个丑数(质因数仅含 2、3、5 的正整数,按升序排列),需满足以下核心需求:
-
丑数序列定义:
- 起始值:
1为第一个丑数; - 生成规则:后续丑数由已有丑数
×2、×3、×5生成(如1→2→3→4→5→6→8→...); - 去重排序:新数需去重并按升序加入序列。
- 起始值:
-
输入约束:
n ∈ [1, 1690](序列最大索引为1690);- 需处理 大索引值(如
n=1690对应丑数2123366400)。
-
计算要求:
- 时间复杂度 O(n)(动态规划 + 三指针);
- 空间复杂度 O(n)(存储前
n个丑数); - 禁止暴力枚举(因数值范围极大)。
-
关键策略:
- 三指针法:
- 指针
p2、p3、p5分别标记×2、×3、×5的基准丑数索引; - 每次取
min(dp[p2]*2, dp[p3]*3, dp[p5]*5)作为新丑数,并更新对应指针。
- 指针
- 三指针法:
-
边界处理:
n=1时返回1;- 序列无重复值(如
6=2×3=3×2仅计数一次); - 示例:
n=7→8(序列:[1,2,3,4,5,6,8]);n=10→12。
输入:整数 n(如 10)
输出:第 n 个丑数(如 12)。
解题思路
核心思想:动态规划 + 三指针
-
定义状态:
- 使用数组
dp存储丑数序列,dp[0] = 1(第1个丑数为1)。 - 初始化三个指针
p2,p3,p5,分别指向下一个可能通过乘以 2、3、5 得到新丑数的位置。
- 使用数组
-
状态转移:
- 对于每个位置
i(从 1 到 n-1),计算候选丑数:
next2 = dp[p2] * 2,
next3 = dp[p3] * 3,
next5 = dp[p5] * 5。 dp[i]取三者中的最小值,确保丑数序列有序递增。- 更新指针:哪个指针生成了当前最小丑数,该指针后移一位(若多个指针生成相同值,则同时后移,避免重复)。
- 对于每个位置
-
终止条件:
- 填充完
dp[0]到dp[n-1]后,返回dp[n-1]。
- 填充完
时间复杂度 O(n):只需遍历一次数组。 空间复杂度 O(n):使用额外数组存储丑数序列。 避免重复计算:通过指针独立移动,每个丑数只生成一次。
代码实现(Java版)🔥点击下载源码
class Solution {
public int nthUglyNumber(int n) {
// 初始化丑数序列
int[] dp = new int[n];
dp[0] = 1; // 第一个丑数是1
// 初始化三个指针,分别指向乘以2、3、5的下一个候选位置
int p2 = 0, p3 = 0, p5 = 0;
for (int i = 1; i < n; i++) {
// 计算三个指针指向的候选丑数
int next2 = dp[p2] * 2;
int next3 = dp[p3] * 3;
int next5 = dp[p5] * 5;
// 取最小值作为下一个丑数
dp[i] = Math.min(next2, Math.min(next3, next5));
// 更新生成当前丑数的指针(多个同时生成则同时更新)
if (dp[i] == next2) p2++;
if (dp[i] == next3) p3++;
if (dp[i] == next5) p5++;
}
return dp[n - 1];
}
}
代码说明
-
初始化:
dp[0] = 1表示第一个丑数为 1。- 指针
p2,p3,p5初始指向位置 0。
-
循环生成丑数(
i从 1 到n-1):- 计算候选值:根据当前指针位置计算
next2,next3,next5。 - 确定最小丑数:取三个候选值的最小值放入
dp[i]。 - 更新指针:若当前丑数由某个指针生成,则该指针后移(可能同时更新多个指针,避免重复值)。
- 计算候选值:根据当前指针位置计算
-
返回结果:
- 循环结束后,
dp[n-1]即为第n个丑数。
- 循环结束后,

3244

被折叠的 条评论
为什么被折叠?



