LeetCode 264. 丑数 II
题目描述

题意分析
将所有只含质因子
2、3、5的正整数从小到大排列,返回第n个。因子不需要三种都出现,1也被定义为丑数;相同数值无论能用多少种乘法生成,在序列里都只能出现一次。
n表示第几个丑数,不是数值上限,也不是统计不超过n的丑数数量。题目范围为1 <= n <= 1690。
解法:三指针动态生成
核心思路
[!blue]
除了起点
1,任何丑数都能除去一个因子2、3或5,得到更小的丑数;反过来,丑数乘这三个因子也仍然是丑数。因此可以从已经生成的结果构造后继,无需逐个检查普通整数。令
dp保存严格递增的丑数序列。把其中每一项分别乘2、3、5,就得到三条有序候选序列。p2、p3、p5分别指向各自序列中尚未写入答案的最小候选,当前候选是dp[p2] * 2、dp[p3] * 3、dp[p5] * 5。下一项就是三个候选中的最小值:任何尚未生成的丑数都来自某条序列,不可能小于这条序列当前最小的未处理项;而这三个候选本身又都是合法丑数。取最小值便同时保证有序性和不漏数。
同一个值可能来自多条序列。写入一次后,所有产生该值的指针都必须前进,否则下一轮还会看到已写入的值,导致重复计数。因此三个推进条件必须是独立的
if,并且比较的是本轮事先保存的三个候选。从
dp[0] = 1、三个指针均为零开始,重复生成到下标n - 1即可。每个指针的推进速度不同,必须各自维护,不能合并成一个位置。
解题步骤
- 创建长度为
n的dp,令dp[0] = 1,并将p2、p3、p5初始化为零。- 从
i = 1到n - 1,先计算并保存三条序列的当前候选。- 取三个候选的最小值写入
dp[i]。- 分别检查每个候选是否等于
dp[i],相等就推进对应指针;可能同时推进多个。- 返回
dp[n - 1]。当n = 1时不进入循环,直接得到起点。
代码实现
class Solution {
public int nthUglyNumber(int n) {
int[] dp = new int[n];
dp[0] = 1;
int p2 = 0;
int p3 = 0;
int p5 = 0;
for (int i = 1; i < n; i++) {
int next2 = dp[p2] * 2;
int next3 = dp[p3] * 3;
int next5 = dp[p5] * 5;
int next = Math.min(next2, Math.min(next3, next5));
dp[i] = next;
// 所有产生当前最小值的指针都要前进,不能写成互斥分支。
if (next == next2) {
p2++;
}
if (next == next3) {
p3++;
}
if (next == next5) {
p5++;
}
}
return dp[n - 1];
}
}
func nthUglyNumber(n int) int {
dp := make([]int, n)
dp[0] = 1
p2, p3, p5 := 0, 0, 0
for i := 1; i < n; i++ {
next2 := dp[p2] * 2
next3 := dp[p3] * 3
next5 := dp[p5] * 5
next := min(next2, min(next3, next5))
dp[i] = next
// 所有产生当前最小值的指针都要前进,不能写成互斥分支。
if next == next2 {
p2++
}
if next == next3 {
p3++
}
if next == next5 {
p5++
}
}
return dp[n-1]
}
复杂度分析
- 时间复杂度:
O(n)。共生成n - 1个新丑数,每轮只做常数次比较和指针移动。- 空间复杂度:
O(n)。三个指针需要访问此前生成的丑数,因此保存长度为n的序列。
关键点总结
[!green]
- 丑数集合稀疏,应从已有丑数构造后继,而不是逐个检查普通整数。
- 本题本质是合并三条有序生成链,三个指针各自记录消费进度。
- 候选相等时必须推进所有来源,才能保证结果严格递增。
dp[0] = 1是生成序列的起点;第n个数对应下标n - 1。- 本题约束下,生成值和本轮候选均可用
int表示。
易错点总结
[!yellow]
- 用
if / else if推进指针,会只消耗同值的一个来源,下一轮再次写入该值。- 忘记把
1作为起点,会使整个序列的生成和序号错位。- 第
n个丑数位于dp[n - 1],访问dp[n]会越界。- 应先保存本轮候选与最小值,再统一推进指针;推进后重算会混入下一轮候选。
- 三条序列的消费速度不同,共用一个指针会跳过尚未选中的较小候选。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 313. 超级丑数 | 中等 | 把固定因子2、3、5推广为给定质数集合,仍需合并候选并跳过重复生成值。 |
| 面试题 17.09. 第 k 个数 | 中等 | 三指针方法相同,原题允许的因子为3、5、7,候选乘法需按范围使用宽整数。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!