264. 丑数 II(优先队列三指针)

Posted mp-ui

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了264. 丑数 II(优先队列三指针)相关的知识,希望对你有一定的参考价值。

264. 丑数 II

难度中等700收藏分享切换为英文接收动态反馈

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

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

示例 1:

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

示例 2:

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

提示:

  • 1 <= n <= 1690

法一:优先队列

最直观的方法,先创建一个优先队列,因为第一个丑数是1,所以把1加入优先队列。接下来就不断地取出优先队列的第1个数,再分别乘以2/3/5放入。

这样,第n个取出的就是我们要的答案。

为了去除重复,我们用一个last来代表我们上一步取出的值,如果这一次取出的和上一次取出的一样的话就不计数。

代码:

class Solution {
    public int nthUglyNumber(int n) {
        PriorityQueue<Long> queue = new PriorityQueue<>();
        queue.add(1L);
        int cnt = 0;
        long last = 0;  //上一个取出的
        while(!queue.isEmpty()){
            long i = queue.poll();
            if(i == last){
                //避免重复
                continue;
            }
            last = i;
            ++cnt;
            if(cnt == n){
                return (int)i;
            }
            queue.offer(i * 2);
            queue.offer(i * 3);
            queue.offer(i * 5);
        }
        return 0;
    }
}

运行结果:

法二:三指针

思路:丑数只包含2/3/5这三个因子,且第一个丑数是1,那么就可以从第一个丑数开始,每一个都分别乘以2/3/5而得到新的丑数。

先定义dp[i] 为第i个丑数,根据定义,dp[1] = 1,为了保证数组是递增的,采取以下方法:

  • 定义三个指针:index2/index3/index5,表示还没有乘过2/3/5的最小丑数
  • dp[index2]*2/dp[index3]*3/dp[index5]*5就是我们这一轮生成的3个丑数
  • 在生成的三个丑数中取出最小的那个放到dp[i]的位置,并把对应的index++

同时还要注意去重

代码:

class Solution {
    public int nthUglyNumber(int n) {
        int[] dp = new int[n + 1];
        dp[1] = 1;  //第一个丑数是1
        int index2 = 1; //还没乘过2的最小丑数的位置
        int index3 = 1; //还没乘过3的最小丑数的位置
        int index5 = 1; //还没乘过5的最小丑数的位置
        for (int i = 2; i <= n; i++) {
            int a = dp[index2] * 2;
            int b = dp[index3] * 3;
            int c = dp[index5] * 5;
            dp[i] = Math.min(a, Math.min(b, c));
            if(dp[i] == a){
                ++index2;
            }else if(dp[i] == b){
                ++index3;
            }else{
                ++index5;
            }
            //去重
            if(dp[i] <= dp[i-1]){
                --i;
            }
        }
        return dp[n];
    }
}

运行结果:

延伸题目:313. 超级丑数

难度中等174收藏分享切换为英文接收动态反馈

超级丑数 是一个正整数,并满足其所有质因数都出现在质数数组 primes 中。

给你一个整数 n 和一个整数数组 primes ,返回第 n超级丑数

题目数据保证第 n超级丑数32-bit 带符号整数范围内。

示例 1:

输入:n = 12, primes = [2,7,13,19]
输出:32 
解释:给定长度为 4 的质数数组 primes = [2,7,13,19],前 12 个超级丑数序列为:[1,2,4,7,8,13,14,16,19,26,28,32] 。

示例 2:

输入:n = 1, primes = [2,3,5]
输出:1
解释:1 不含质因数,因此它的所有质因数都在质数数组 primes = [2,3,5] 中。

提示:

  • 1 <= n <= 106
  • 1 <= primes.length <= 100
  • 2 <= primes[i] <= 1000
  • 题目数据 保证 primes[i] 是一个质数
  • primes 中的所有值都 互不相同 ,且按 递增顺序 排列

n指针解法

思路和上面的三指针是一样的,上面是只定义三个指针,这次直接定义primes.length个指针

代码:

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

还有就是关于去重,到这里我就想明白了,上面的三指针的代码是这样子的:

因为dp数组本身就是按照从小到大的顺序排的,其实有可能解出的a/b/c是一样的,只要把下面的三个if判断都改成独立的就行了,不要else if这种形式,就可以去重。

int a = dp[index2] * 2;
int b = dp[index3] * 3;
int c = dp[index5] * 5;
dp[i] = Math.min(a, Math.min(b, c));
if(dp[i] == a){
    ++index2;
}else if(dp[i] == b){
    ++index3;
}else{
    ++index5;
}
//去重
if(dp[i] <= dp[i-1]){
    --i;
}

运行结果:

以上是关于264. 丑数 II(优先队列三指针)的主要内容,如果未能解决你的问题,请参考以下文章

[M思维] lc264. 丑数 II(多路归并+STL堆)

leetcode中等264丑数2

java刷题--264丑数II

java刷题--264丑数II

264丑数II

LeetCode(算法)- 264. 丑数 II