Go&Java算法之丑数

128 阅读2分钟

这是我参与11月更文挑战的第21天,活动详情查看:2021最后一次更文挑战

丑数

我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。

 

示例:

输入: n = 10 输出: 12 解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。 说明:  

1 是丑数。 n 不超过1690。

题解

方法一:动态规划——Java

根据题目对 丑数 的定义。

我们可以知道,用 丑数 * (2、3、5) 会得到另外三个丑数。

也就是说:一个丑数生成3个新丑数。

我们定义三个变量: a2 a3 a5三个变量表示 当前丑数 是否使用 2 3 5 这三个数生成新的丑数; 当一个丑数使用过 2 3 5 中的任意的一个数后,就 没有资格 再用它和 使用过的数 去生成新丑数。

比如: 丑数A, (A * 2)会生成一个新的丑数,那么 丑数A 就没有资格再与 2 相乘得到另一个丑数,只能与 3、5 。

那么每次我们从 将要生成的丑数中找出最小的那个进行排序,就能得到一个丑递增顺序数组。 当生成第 n 个丑数后,直接返回就可以。

比如从1开始:

a2 a3 a5 分别表示 丑数1 还没有被 2 3 5 使用,生成新的丑数。

从将要生成新的丑数里面选一个最小的,当最新的丑数,也就是 2。

此时丑数1 的 a2 被使用过了,那么 a2 就指向下一个 丑数2。

此时 a2 表示 丑数2 没使用 2 来新成丑数,a3 a5 表示 丑数1 还没有被 3 5 使用,生成新的丑数。

依次往下....

class Solution {
    public int nthUglyNumber(int n) {
        int[] dp = new int[n];
        int a = 0, b = 0, c = 0;
        dp[0] = 1;
        for (int i = 1; i < n; i++) {
            int f2 = dp[a] * 2;
            int f3 = dp[b] * 3;
            int f5 = dp[c] * 5;
            dp[i] = Math.min(Math.min(f2, f3), f5);
            if (dp[i] == f2) {
                a++;
            }
            if (dp[i] == f3) {
                b++;
            }
            if (dp[i] == f5) {
                c++;
            }
        }
        return dp[n - 1];
    }
}

方法一:动态规划——Go

func nthUglyNumber(n int) int {
    dp := make([]int, n+1)
    dp[1] = 1
    p2, p3, p5 := 1, 1, 1
    for i := 2; i <= n; i++ {
        x2, x3, x5 := dp[p2]*2, dp[p3]*3, dp[p5]*5
        dp[i] = min(min(x2, x3), x5)
        if dp[i] == x2 {
            p2++
        }
        if dp[i] == x3 {
            p3++
        }
        if dp[i] == x5 {
            p5++
        }
    }
    return dp[n]
}

func min(a, b int) int {
    if a < b {
        return a
    }
    return b
}