力扣(LeetCode) 【每日一题】 313.超级丑数 2021.8.9

本文介绍了一种解决求解第n个超级丑数问题的优化方法,通过使用指针和动态规划降低时间和空间复杂度。首先通过优先队列(二叉堆)实现基本解法,然后引入指针数组来跟踪每个质因数对应的丑数索引,通过状态转移方程更新dp数组,从而提高效率。这种方法适用于解决类似题目。
摘要由CSDN通过智能技术生成

题目链接:

313.超级丑数

不想戳的看下图:

在这里插入图片描述

解题思路1:
用题目给的例子 [2,7,13,19] 来说。
将 [2,7,13,19] 依次入堆。
出堆一个数字,也就是 2。这时取到了第一个超级丑数。
接着将 2 和 [2,7,13,19] 的乘积,也就是 [4,14,26,38] 依次入堆。这样就可以得到其他的丑数。
每次堆都可以取到最小的,每次我们也会将最小的从堆中移除。因此取 n 次自然就是第 n 大的超级丑数了。

注:
堆的解法没有太大难度,唯一需要注意的是去重。比如 2 * 13 = 26,而 13 * 2 也是 26。
去重可以每次拿堆顶元素先前出堆的数比较,一样则弹出堆。

代码如下:

class Solution {
    public int nthSuperUglyNumber(int n, int[] primes) {
        // 优先队列(底层实现为二叉堆)
        PriorityQueue<Long> queue = new PriorityQueue<>();
        // 统计是第几个丑数
        int count = 1;
        // 返回的结果
        long res = 1;
        queue.add(res);
        while (count <= n) {
            res = queue.poll();
            // 将优先队列中的和res一样的数去重,避免重复统计相同的丑数
            while (!queue.isEmpty() && res == queue.peek()) {
                queue.poll();
            }  
            count++;
            // 将此时的最小丑数与所有质数相乘,再加入新的优先队列中
            for (int i = 0; i < primes.length; i++) {
                queue.offer(res * primes[i]);
            }
        }
        return (int )res;
    }
}

这种思路当然可以,但时间和空间效率较低。
于是我们有下一种解法:。

解题思路2:

我们用到指针

为什么会想到指针?
1.丑数简单来说就是质因数的乘积,因为是正序排序的丑数,后面的丑数一定是由前面已得出的丑数乘上某个质因数
2.由于正序的束缚,我们需要这个乘积是这k个质因数各自前面已得出的丑数的k个乘积最小的。
3.很自然的我们就可以联想到要储存这k个质因数对应的前面已得出的丑数,而这些前面已得出的丑数是由dp数组内的索引表示的。

我们利用一个指针数组points来储存和更新这k个质因数对应的前面已得出的丑数的dp索引

为了计算方便,我们同时可以设置一个备选丑数数组val来储存和更新当前所有备选的丑数,即k个质因数与对应前面得出的丑数的乘积

而其中最小的那个备选项就是我们需要的下一个丑数。

状态转移方程:dp[i] = min(dp[points[k]] * primes[k])

代码如下:

class Solution {
    public int nthSuperUglyNumber(int n, int[] primes) {
        int k=primes.length;
        int[] dp=new int[n+1],points=new int[k],val=new int[k];
        Arrays.fill(points,1);
        dp[1]=1;
        for(int i=2;i<=n;i++) {
            int min=Integer.MAX_VALUE;
            for(int j=0;j<k;j++) {
                val[j]=dp[points[j]]*primes[j];
                min=Math.min(min,val[j]);
            }
            dp[i]=min;
            for(int j=0;j<k;j++) {
                if(min==val[j]) {
                    points[j]++;
                }
            }
        }
        return dp[n];
    }
}

小结

本题为第264题的扩展,但核心思路都是相同的,即指针的巧用。只要学会这种方法,同类题都能迎刃而解!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值