丑数系列算法

1. 丑数 I

题目: 丑数就是只包含质因数 235 的正整数。给定一个整数 n ,请判断 n 是否为丑数 。如果是,返回 true ;否则,返回 false

示例1:

输入:n = 6
输出:true
解释:6 = 2 × 3

示例2:

输入:n = 1
输出:true
解释:1 没有质因数,因此它的全部质因数是 {2, 3, 5} 的空集。习惯上将其视作第一个丑数。

示例3:

输入:n = 14
输出:false
解释:14 不是丑数,因为它包含了另外一个质因数 7 。

比如 12 = 2 x 2 x 3 就是一个丑数,而 42 = 2 x 3 x 7 就不是一个丑数。

题一解题思路

class Solution {
    public boolean isUgly(int n) {
        if(n<=0) return false;
		
        //任意一个大于1的丑数(正整数)都可以分解成若干质数的乘积,并且这些质数只能是 2, 3 或 5。
        while(n%2==0) return isUgly(n/2);
        while(n%3==0) return isUgly(n/3);
        while(n%5==0) return isUgly(n/5);

        if(n==1) return true;
        else return false;
    }
}

2. 丑数 II

题目: 给定一个整数 n ,请你找出并返回第 n 个丑数 。丑数为只包含质因数 235 的正整数。

示例1:

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

示例2:

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

题二解题思路

class Solution {
    public int nthUglyNumber(int n) {
        int a=1,b=1,c=1;  //a,b,c分别表示p2,p3,p5链表当前的结点,链表头节点均为1
        int p2=1,p3=1,p5=1; //有序链表的指针
        int[] ugly = new int[n+1]; //丑数集合
        int count = 1; // 计数器
	
        //合并三个有序链表,直到找到第n个丑数时结束
        while(count<=n){
            int min = Math.min(Math.min(a,b),c);
            ugly[count++] = min;
            //很巧妙的点,p2,p3,p5链表都是针对丑数集合内的元素进行倍乘
            if(a==min) a=2*ugly[p2++];
            if(b==min) b=3*ugly[p3++];
            if(c==min) c=5*ugly[p5++];
        } 
        return ugly[n];
    }
}

3. 超级丑数(丑数 II 的进阶版)

题目: 超级丑数是一个正整数,并满足其所有质因数都出现在质数数组 primes 中。给定一个整数 n 和一个整数数组 primes ,返回第 n 个 超级丑数 。

示例 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] 中。

题三解题思路

「往后产生的丑数」都是基于「已有丑数」而来(使用「已有丑数」 * 「给定质因数」primes[i]

​ 题二质因数个数为3个,我们维护了三个链表 p2 , p3 , p5。同理,如果 primes 的长度为 m 的话,我们可以使用 m 个指针来指向这 m 个有序序列 arr 的当前下标。当然,实现上,我们并不需要构造出这m个有序序列。

我们可以构造一个存储三元组的小根堆,三元组信息为(val , i , index)

  • val :当前指针所指向的值。
  • i : 表示primes[]里面第 i 个元素(即第 i 个有序列表)。
  • index :代表丑数下标,存在关系:val = ugly[index] * primes[i]。
class Solution {
    public int nthSuperUglyNumber(int n, int[] primes) {
        //构造优先队列,好好体会
        PriorityQueue<int[]> pq = new PriorityQueue<>((a,b) -> a[0]-b[0]);

        //将m个有序队列头结点加入优先队列中
        for(int i=0;i<primes.length;i++){
            pq.add(new int[]{1,i,1});
        }
        int count = 1;
        int[] ugly = new int[n+1];

        while(count<=n){
            int[] temp = pq.poll();
            int val = temp[0];
            int i = temp[1];
            int index = temp[2];
            if(val != ugly[count-1]){
                ugly[count] = val;
                count++;
            }
            pq.add(new int[]{ugly[index]*primes[i],i,index+1});
        }
        return ugly[n];
    }
}

4. 丑数 III

题目:

输入:n=3, a=2, b=3, c=5
输出:4
解释:丑数序列为2,3,4,5,6,8,9,10… 其中第3个是4。

​ 这道题和之前题目的不同之处在于它改变了丑数的定义,只要一个正整数x存在a, b, c中的任何一个因子,那么x就是丑数。

二分搜索

posted @ 2022-09-13 19:55  JH_KingHau  阅读(94)  评论(0编辑  收藏  举报