剑指 Offer 49. 丑数

思路一:

利用小根堆。

小根堆弹出的数是堆里最小的,于是弹出一个x。

就将2*x,3*x,5*x加入set去重,然后重新加入小根堆。

弹出的第n个数就是第n个丑数。

class Solution {
    public int nthUglyNumber(int n) {
        int[] factors = {2, 3, 5};

        Queue<Long> heap = new PriorityQueue<>();
        HashSet<Long> set = new HashSet<>();
        heap.offer(1L);
        set.add(1L);

        int res = 0;
        for(int i = 0; i < n; i++){
            long cur = heap.poll();
            //记录当前弹出的数
            res = (int)cur;
            //弹一个数x,将2*x,3*x,5*x,先放入set去重,然后加入进小根堆
            for(int nums : factors){
                if(set.add(nums * cur))
                    heap.offer(nums * cur);
            }
        }
        return res;
    }
}

思路二:动态规划

1.状态定义

dp[i]表示第i + 1个丑数

2.状态转移方程

dp[i]=min(dp[a]×2,dp[b]×3,dp[c]×5)

 

class Solution {
    public int nthUglyNumber(int n) {
        int[] dp = new int[n+1];
        dp[0] = 1;
        int a = 0, b = 0, c = 0;
        //因为因子相同,丑数可以从之前的丑数得出
        //下一个丑数必定是之前某个丑数*2或者*3或者*5得出
        //于是从小到大遍历丑数,比较每个丑数*2,*3,*5的结果
        //dp[i]=min(dp[a]×2,dp[b]×3,dp[c]×5)
        for (int i = 1; i < n; i++){
            int num2 = dp[a] * 2, num3 = dp[b] * 3, num5 = dp[c] * 5;
            dp[i] = Math.min(Math.min(num2, num3), num5);
            //可能会有多个指针同时向后退
            //因为num2可能等于num3类似的
            if(dp[i] == num2) a++;
            if(dp[i] == num3) b++;
            if(dp[i] == num5) c++;
        }
        //dp[i]表示第i + 1个丑数的值,因为dp[0]是第一个丑数为1
        return dp[n - 1];
    }
}

剑指 Offer 49. 丑数

posted @ 2021-04-14 21:48  星予  阅读(41)  评论(0编辑  收藏  举报