丑数系列算法
1. 丑数 I
题目: 丑数就是只包含质因数 2
、3
和 5
的正整数。给定一个整数 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
个丑数 。丑数为只包含质因数 2
、3
或 5
的正整数。
示例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
就是丑数。