题目描述
把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
思路一
依次判断每个数是不是丑数,直到判断到第N个丑数为止,时间复杂度过大,不推荐
public boolean isUglyNumber(int number){
while (number % 2 == 0){
number /= 2;
}
while (number % 3 == 0){
number /= 3;
}
while (number % 5 == 0){
number /= 5;
}
return (number == 1)? true:false;
}
public int GetUglyNumber_Solution_1(int index){
int number = 0;
int count = 0;
while (count < index){
number++;
if(isUglyNumber(number)){
count++;
}
}
return number;
思路二
通俗易懂的解释:
首先从丑数的定义我们知道,一个丑数的因子只有2,3,5,那么丑数p = 2 ^ x * 3 ^ y * 5 ^ z
- 换句话说一个丑数一定由另一个丑数乘以2或者乘以3或者乘以5得到
- 或者说一个丑数乘以2or3or5一定会是另外一个丑数
那么我们从1开始乘以2,3,5,就得到2,3,5三个丑数,在从这三个丑数出发乘以2,3,5就得到(4,6,10),(6,9,15),(10,15,25)九个丑数,我们发现这种方法会得到重复的丑数,而且我们题目要求第N个丑数,这样的方法得到的丑数也是无序的。
-
所以我们采用另外一种方法,设顺序排列的N个丑数的数组为result[N]
-
首先从最小的丑数:reslut[0]=1开始,分别乘2,3,5得到三个数,然后选择最小的数2放入result[1] = 2
那么reslut[2]一定是由前面的1和2分别乘上2,3,5某个数得到的,但是我们还不知道具体是哪个,所以需要将2乘上2,3,5,然后放入候选队列中,不过这里放入候选队列是有讲究的,所有乘以2的数在一个队列,所有乘以3的数在一个队列,所有乘以5的数在一个队列
那么我们可以维护三个队列:
(1)丑数数组: 1
乘以2的队列:2
乘以3的队列:3
乘以5的队列:5
选择三个队列头最小的数2加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;
(2)丑数数组:1,2
乘以2的队列:4
乘以3的队列:3,6
乘以5的队列:5,10
选择三个队列头最小的数3加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;
(3)丑数数组:1,2,3
乘以2的队列:4,6
乘以3的队列:6,9
乘以5的队列:5,10,15
选择三个队列头里最小的数4加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;
(4)丑数数组:1,2,3,4
乘以2的队列:6,8
乘以3的队列:6,9,12
乘以5的队列:5,10,15,20
选择三个队列头里最小的数5加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;
(5)丑数数组:1,2,3,4,5
乘以2的队列:6,8,10,
乘以3的队列:6,9,12,15
乘以5的队列:10,15,20,25
选择三个队列头里最小的数6加入丑数数组,但我们发现,有两个队列头都为6,所以我们弹出两个队列头,同时将12,18,30放入三个队列;
public class Solution {
public int GetUglyNumber_Solution(int index) {
if(index<=0)
return 0;
int[] result = new int[index];
int count = 0;
int i2 = 0;
int i3 = 0;
int i5 = 0;
result[0] = 1;
int tmp = 0;
while (count < index-1) {
//这里的result[i2] result[i3] result[i5] 分别代表三个队列的的第一个数,初始值都为1
//他们分别乘以2,3,5,作为下一个的丑数候选者
//事实上运行的时候每个队列里面只有一个数
tmp = min(result[i2] * 2, min(result[i3] * 3, result[i5] * 5));
//从候选者中选出最小的数作为下一个丑数,然后这个时候该队列的下标就向后移动一个,指向该队列的下一个候选者(而下一个候选者 = 当前队列的下标在result数组中指向的数 * 队列的类型数2or3or5)
if(tmp==result[i2] * 2) i2++;//三条if防止值是一样的,不要改成else的
if(tmp==result[i3] * 3) i3++;
if(tmp==result[i5] * 5) i5++;
result[++count]=tmp;
}
return result[index - 1];
}
private int min(int a, int b) {
return (a > b) ? b : a;
}
}
解析
import java.util.Arrays;
public class Test14 {
public static void main(String[] args){
Test14 test14 = new Test14();
int result = test14.GetUglyNumber_Solution(5);
System.out.println(result);
}
public int GetUglyNumber_Solution(int index) {
if(index<=0)
return 0;
int[] result = new int[index];
int count = 0;
int i2 = 0;
int i3 = 0;
int i5 = 0;
result[0] = 1;
int tmp = 0;
while (count < index-1) {
int i3Value = result[i3] * 3;
System.out.println("i3Value:" + i3Value);
int i5Value = result[i5] * 5;
System.out.println("i5Value:" + i5Value);
int i2Value = result[i2] * 2;
System.out.println("i2Value:" + i2Value);
tmp = min(i2Value, min(i3Value, i5Value));
System.out.println("最小值:" + tmp);
if(tmp==result[i2] * 2) i2++;//三条if防止值是一样的,不要改成else的
System.out.println("i2:" + i2);
if(tmp==result[i3] * 3) i3++;
System.out.println("i3:" + i3);
if(tmp==result[i5] * 5) i5++;
System.out.println("i5:" + i5);
result[++count]=tmp;
System.out.println("resul" + Arrays.toString(result));
}
return result[index - 1];
}
private int min(int a, int b) {
return (a > b) ? b : a;
}
}
i3Value:3
i5Value:5
i2Value:2
最小值:2
i2:1
i3:0
i5:0
resul[1, 2, 0, 0, 0]
i3Value:3
i5Value:5
i2Value:4
最小值:3
i2:1
i3:1
i5:0
resul[1, 2, 3, 0, 0]
i3Value:6
i5Value:5
i2Value:4
最小值:4
i2:2
i3:1
i5:0
resul[1, 2, 3, 4, 0]
i3Value:6
i5Value:5
i2Value:6
最小值:5
i2:2
i3:1
i5:1
resul[1, 2, 3, 4, 5]
5
Process finished with exit code 0