在问 · 算法题解

264. 丑数 II

中等动态规划三指针动态规划力扣原题 ↗

题目描述

丑数指的是质因数只有 2、3、5 的正整数,1 也视为丑数。

输入一个整数 n。把所有丑数从小到大排成一列,返回其中第 n 个(从 1 开始计数)。

示例 1
输入:n = 10
输出:12

解释:前 10 个丑数是 1, 2, 3, 4, 5, 6, 8, 9, 10, 12,第 10 个是 12。

示例 2
输入:n = 1
输出:1

解释:第 1 个丑数是 1。

数据范围

  • 1 <= n <= 1690

解题思路:三指针动态规划

每个丑数(除 1 外)都是更小的某个丑数乘 2、3 或 5 得到的。把丑数序列 dp 看成三条有序流:dp*2、dp*3、dp*5,用三个指针 p2、p3、p5 指向各流中下一个待用的位置,每次取三者最小值作为下一个丑数,并把所有等于它的指针前移(去重)。

为什么正确

dp[0..i-1] 是前 i 个丑数(严格递增);dp[p2]*2、dp[p3]*3、dp[p5]*5 都大于 dp[i-1],且是各流中最小的未用值。

复杂度分析

时间 O(n),空间 O(n)。

易错点

三个 if 必须是并列的而不是 elif:多个候选同时等于最小值时(如 6=2*3=3*2)要一起前移,否则会出现重复的丑数。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def nthUglyNumber(self, n: int) -> int:
        dp = [1] * n
        p2 = p3 = p5 = 0
        for i in range(1, n):
            a, b, c = dp[p2] * 2, dp[p3] * 3, dp[p5] * 5
            dp[i] = min(a, b, c)
            if dp[i] == a:
                p2 += 1
            if dp[i] == b:
                p3 += 1
            if dp[i] == c:
                p5 += 1
        return dp[n - 1]

载入编辑器,自己跑一遍 →

其他解法

最小堆先生成候选再挑最小值,O(n log n);三指针利用“每个丑数都由更小的丑数乘 2、3、5 得到”,按序合并三个序列,O(n) 且自带去重。

解法 2:最小堆 + 哈希去重 时间 O(n log n),空间 O(n)

最小堆初始只有 1。每次弹出堆顶 x,它就是下一个最小的丑数;再把 2x、3x、5x 中没见过的放入堆,用哈希集合去重。第 n 次弹出的数就是答案。

思路直观,容易推广到任意质因子集合。堆操作 O(log n),还要额外的去重集合;堆中的数会超过答案范围,需要用 64 位整数。

import heapq

class Solution:
    def nthUglyNumber(self, n: int) -> int:
        heap = [1]
        seen = {1}
        for _ in range(n - 1):
            x = heapq.heappop(heap)
            for p in (2, 3, 5):
                y = x * p
                if y not in seen:
                    seen.add(y)
                    heapq.heappush(heap, y)
        return heap[0]

动态规划 专题的其他题目

查看 动态规划 全部题目 →