LeetCode 剑指 Offer 49. 丑数
题目描述


题意分析
丑数是质因数只可能包含
2、3、5的正整数,并规定1是第一个丑数。按从小到大、不重复的顺序排列这些数,求第n个。不要求三个质因数都出现,只要没有其他质因数即可。题图求的是第 1500 个,下面把序号写成参数
n,用同一种生成方法求指定位置。
解法:三指针动态规划
核心思路
[!blue]
每个大于一的丑数都至少含有一个因子
2、3或5。去掉其中一个因子后,剩下的仍是更小的丑数;反过来,已有丑数乘这三个数之一,也一定还是丑数。因此所有后续答案都能从已生成的序列中得到。把丑数序列分别乘以
2、3、5,就得到三条各自递增的候选序列。用p2、p3、p5分别指向每条序列中尚未输出的最小候选,对应值为dp[p2] * 2、dp[p3] * 3、dp[p5] * 5。下一项必须是三路候选中的最小值:它既是合法丑数,也不会跳过任何更小的未输出丑数。写入后,凡是候选等于这一项的指针都要前进。每条候选序列内部严格递增,前进一步后就越过了刚输出的值;不同生成方式得到的同一个数也因此只输出一次。
没命中的指针保持不动,因为它仍代表该路最小的未使用候选。每轮之后,三路指针的含义继续成立,所以可以一直合并到第
n项。起点必须为dp[0] = 1,最后返回零基下标n - 1。
解题步骤
- 创建长度为
n的结果序列,令dp[0] = 1,三个指针都从下标零开始。- 计算三路乘积,取最小值写入当前答案位置。
- 使用三个独立判断,推进所有候选等于该最小值的指针。
- 生成到第
n项后返回dp[n - 1];n == 1时直接由初始化得到答案。
代码实现
class Solution {
// 三个指针分别指向下一个可以乘 2、乘 3、乘 5 的已有丑数,每轮取三个候选中的最小值。
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 {
// 三个指针分别指向下一个可以乘 2、乘 3、乘 5 的已有丑数,每轮取三个候选中的最小值。
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 := next2
if next3 < next {
next = next3
}
if next5 < next {
next = next5
}
// 三条递增候选中的最小值,就是下一项丑数。
dp[i] = next
// 所有命中最小值的指针都推进,独立判断避免重复产出。
if next == next2 {
p2++
}
if next == next3 {
p3++
}
if next == next5 {
p5++
}
}
return dp[n-1]
}
复杂度分析
- 时间复杂度:$O(n)$,每轮只计算三个候选并进行常数次比较。
- 空间复杂度:$O(n)$,保存三个指针仍可能访问的历史丑数。
关键点总结
[!green]
- 去掉一个允许的质因数后仍是丑数,保证三路生成不会遗漏。
- 指针指向每一路最小的未输出候选,而不是统一指向上一项。
- 所有命中最小值的指针都推进,去掉跨序列的重复值。
易错点总结
[!yellow]
- 把三个判断写成
else if,只会推进一路,另一条路下一轮还会重复输出相同值。- 只把上一项乘二、三、五,会跳过较早历史项生成的更小候选。
- 初始值不能为零,否则全部乘积都停留在零;一是生成整个序列的起点。
- 第一个丑数编号为一,而数组下标从零开始,返回位置应为
n - 1。- 不能要求一个丑数同时包含二、三、五,允许其中某些因子的指数为零。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 313. 超级丑数 | 中等 | 把固定因子2、3、5推广为给定质数集合,仍需合并候选并跳过重复生成值。 |
| 面试题 17.09. 第 k 个数 | 中等 | 三指针方法相同,原题允许的因子为3、5、7,候选乘法需按范围使用宽整数。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!