把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
思路1:硬算
对每一个数字都循环判断,是否能被2,3,5整除为1。如果是,那就是丑数;如果不是,那就不是丑数。
这种方法由于过于硬核,所以,时间复杂度比较大,而且牛客网是通不过的。优化的思路就是牺牲一定的空间,存储之前算好的丑数,然后用已有的丑数推导出之后的丑数。
public class Solution {
public int GetUglyNumber_Solution1(int index) {
if (index <= 0) {
return 0;
}
int num = 0;
int count = 0;
while (count < index){
num++;
if (isUgly(num)) {
count ++;
}
}
return num;
}
private boolean isUgly(int num){
while(num % 2 == 0)
num /= 2;
while(num % 3 == 0)
num /= 3;
while(num % 5 == 0)
num /= 5;
return num == 1 ? true : false;
}
}
思路2: 推导法
由丑数的定义可以知道,第M个丑数是由前M - 1个丑数中的某一个数 *2, *3, *5 得来的。比如对于丑数数组 [1, 2, 3, 4, 5, 6, 8] , 2 = 1 * 2 , 3 = 1 * 3, 4 = 1 * 2 * 2, 5 = 1 * 5, 。。。
所以,只要我们记录第1 个丑数,然后对它进行 * 2, *3, *5 操作,然后比较取结果最小的那个,就是第二个丑数;然后对第 2 个丑数进行 * 2, *3, *5 的操作,比较结果取最小的那个,就是第三个丑数,以此类推,直到找到第 n 个丑数,就找到了我们期望的结果。
代码如下:
public class Solution {
private int Min(int num1, int num2, int num3){
int n = num1 < num2 ? num1 : num2;
int nn = n < num3 ? n : num3;
return nn;
}
public int GetUglyNumber_Solution(int index) {
if (index <= 0)
return 0;
int[] ugly = new int[index];
ugly[0] = 1;
int nextIndex = 1;
int index2 = 0;
int index3 = 0;
int index5 = 0;
while(nextIndex < index){
int min = Min(ugly[index2] * 2, ugly[index3] * 3, ugly[index5] * 5);
ugly[nextIndex] = min;
while(ugly[index2] * 2 <= ugly[nextIndex])
index2 ++;
while(ugly[index3] * 3 <= ugly[nextIndex])
index3 ++;
while(ugly[index5] * 5 <= ugly[nextIndex])
index5 ++;
nextIndex += 1;
}
return ugly[nextIndex - 1];
}
}