leetcode-丑数 II

253 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

假期余额不足,上午起来就已经从长假退化成普通周末了。今天杭州天气终于凉快了一些,终于可以出去逛逛了。今天继续学习动态规划。

题目

给你一个整数 n ,请你找出并返回第 n 个 丑数 。

丑数 就是只包含质因数 2、3 和/或 5 的正整数。

示例 1:
输入:n = 10
输出:12
解释:[1, 2, 3, 4, 5, 6, 8, 9, 10, 12] 是由前 10 个丑数组成的序列。

示例 2:
输入:n = 1
输出:1
解释:1 通常被视为丑数。

思路

今天的题目跟之前的类型不太一样,不是那种简单的从dp[i]推导dp[i+1]的。
因为这题没做出来,对这个解法印象还比较深,所以今天也能写出来,不过后来去看了题解后,发现评论里面有些同学对这个解法还没太理解,所以这里就说说我自己的理解吧。
解法是这样的:定义一个一维数组dp[]来存储丑数,由定义得知,dp[1]=1;然后定义3个指针:p2、p3、p5,初始情况下让这3个指针都指向1。然后从i=2开始向后循环,dp[i] = min(dp[p2]*2, dp[p3]*3, dp[p5]*5),确定dp[i]的值后,再反向去更新指针,如果dp[i] == dp[p2]*2,那么指针p2值+1,如果dp[i] == dp[p3]*3,那么指针p3值+1,如果dp[i] == dp[p5]*5,那么指针p5值+1。

解法不难操作,但是有点难理解为什么这么做是对的。我的想法是:假设现在已经有一段已知的丑数了,是dp[1] ~ dp[k],那么既然dp[1] ~ dp[k]是丑数,那么dp[1]*2 ~ dp[k]*2,dp[1]*3 ~ dp[k]3,dp[1]5 ~ dp[k]5,也一定是丑数,而且反过来,因为丑数是质保函因子2、3、5的,所以除了1之外,任何一个丑数都肯定在上面3个数组中(如果考虑k已经到无限大的话)。所以,我们可以把丑数的数组分成3个子数组:原数组2、原数组3、原数组5,这样分是不会漏的(可能会重复,不过没关系),这样就能理解上面3个指针的解法了,dp[i+1]就是从这3个数组里面,选出最小的1个来。

Java版本代码

class Solution {
    public int nthUglyNumber(int n) {
        int dp[] = new int[n+1];
        dp[1] = 1;
        int p2 = 1, p3 = 1, p5 = 1;
        for (int i = 2; i <= n; i++) {
            dp[i] = Math.min(dp[p2]*2, Math.min(dp[p3]*3, dp[p5]*5));
            if (dp[i] == dp[p2]*2) {
                p2++;
            }
            if (dp[i] == dp[p3]*3) {
                p3++;
            }
            if (dp[i] == dp[p5]*5) {
                p5++;
            }
        }
        return dp[n];
    }
}