leetcode
leetcode 2601 ~ 2650
丑数

丑数

难度:

标签:

题目描述

English description is not available for the problem. Please switch to Chinese.

代码结果

运行时间: 140 ms, 内存: 14.9 MB


/*
题目思路:
使用Java Stream来实现该功能并不常见,因为它不太适合这种需要状态和顺序的操作。
不过我们仍然可以用stream生成无穷序列,然后截取第n个丑数。
我们将使用Stream.iterate和limit来实现。
*/

import java.util.stream.Stream;
import java.util.PriorityQueue;
import java.util.HashSet;

public class UglyNumberStream {
    public int nthUglyNumber(int n) {
        PriorityQueue<Long> heap = new PriorityQueue<>();
        HashSet<Long> seen = new HashSet<>();
        heap.add(1L);
        seen.add(1L);
        long[] primes = {2, 3, 5};
        Stream<Long> stream = Stream.iterate(1L, x -> {
            long currentUgly = heap.poll();
            for (long prime : primes) {
                long newUgly = currentUgly * prime;
                if (seen.add(newUgly)) {
                    heap.add(newUgly);
                }
            }
            return heap.peek();
        }).limit(n);
        return stream.skip(n - 1).findFirst().get().intValue();
    }
}

解释

方法:

该题解采用了动态规划的方法来解决丑数问题。首先创建一个长度为 n 的数组 dp,其中 dp[i] 存储第 i+1 个丑数。初始化 dp[0] 为 1,因为 1 是第一个丑数。接着使用三个指针 a、b、c 分别指向数组中某个确定的位置,这些位置对应的元素乘以 2、3、5 后可以生成新的丑数。每次循环计算出三个可能的丑数 n2, n3, n5(分别为 dp[a]*2, dp[b]*3, dp[c]*5),取这三个数中最小的一个作为新的丑数添加到 dp 数组中。然后根据所选的丑数来更新对应的指针(如果新丑数等于 n2,则移动 a 指针;如果等于 n3,则移动 b 指针;如果等于 n5,则移动 c 指针)。这样可以保证数组 dp 按从小到大的顺序存储每一个丑数,直到填满。最后 dp[n-1] 就是第 n 个丑数。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
为什么在计算新的丑数时,要选择三个计算结果中的最小值来作为下一个丑数?
丑数是由另一个丑数乘以2、3或5得到的。在动态生成丑数的过程中,我们保持数组dp中的每个丑数都是从小到大排列的。因此,每次计算新的丑数时,我们需要从dp[a]*2、dp[b]*3、dp[c]*5这三个候选丑数中选择最小的一个,保证添加到数组中的新丑数是当前能够生成的最小的丑数,这样可以确保整个数组的有序性,且不会遗漏任何一个丑数。
🦆
在更新指针a、b、c时,为什么可能会有多个指针同时移动?
在某个特定的循环中,可能存在两个或三个候选丑数的值相同的情况。例如,如果dp[a]*2和dp[b]*3的结果相同,那么选择这个值作为新的丑数后,指针a和b都应该向前移动,因为它们各自的当前值已经被用于生成了新的丑数。这种同时移动多个指针的做法可以确保不会重复计算相同的丑数,且每个丑数只被用一次来生成新的更大的丑数。
🦆
在初始化dp数组时,为什么只设置dp[0]=1而不是预先计算更多的丑数?
dp数组的初始化为dp[0]=1,因为1被定义为第一个丑数。这种初始化方式可以为算法提供一个明确的起点,并且避免了预先假设必须计算哪些丑数,这样算法可以动态地基于已知的丑数生成新的丑数。如果预先计算更多的丑数可能导致不必要的计算和错误,而动态计算可以确保每步计算都是必要的,并且在任何时候都可以停止算法而不会有资源浪费。
🦆
如果在循环过程中两个或三个丑数的值相同,这种情况如何处理,会不会影响到丑数序列的正确性?
当两个或三个候选丑数的值相同时,算法应该将所有具有相同值的指针都向前移动。这是因为这些指针分别代表了不同因子(2、3、5)生成的候选丑数,即使它们的值相同,也应当视为它们各自的生成结果已被使用过。这样做不会影响丑数序列的正确性,反而能保证每个生成的丑数都是基于数组中正确的、顺序的前一个丑数,并且避免了重复计算同一个值作为新丑数。

相关问题