题目
把只包含因子2、3和5的数称作丑数(Ugly Number)。
例如6、8都是丑数,但14不是,因为它包含因子7。
习惯上我们把1当做是第一个丑数。
求按从小到大的顺序的第N个丑数。
思路:
1. 暴力法:
先判断一个数是不是 丑数,然后从0开始判断,知道符合 丑数的数目 累加到 N 。
2. 三个取最小值法:
一个丑数必然是 2 ,3,5 这个三个因为的 一个 或多个相乘。
初始的几个数为 [ 1,2,3,5 ] 对吗? 不对。
初始的几个数据为 **[ 1,2,3, 4, 5 ]**
4 为二乘了两次。然后呢,是 3*2 =6 。这个 6 又是怎么比较出来的呢。
我用 3*2 ==》 所有2 的倍数,并且因子也是丑数的,因为, 1*2 ,2*2 ,已经用过了,现在只能用 3*2 了。
2*3 ==》 同理,所有 3的倍数,并且因子也是丑数的, 1*3 已经用过了。
2*5 ==》 所有 5的倍数,并且因子也是丑数的, 1*5 已经用过了。
所以上述的方法每次只需要比较三个数。并且不遗不漏。
代码如下:
public class Solution {
public int GetUglyNumber_Solution(int index) {
if(index<=0)return 0;
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(1); // 1
int i2=0;
int i3=0;
int i5=0;
int min=0;
for(int i=1;i<index;i++){
int m2 = list.get(i2)*2;
int m3 = list.get(i3)*3;
int m5 = list.get(i5)*5;
System.out.println("i为 "+i+" , m2 = "+ m2 +" , m3 = "+ m3 +" , m5 = "+ m5 );
System.out.println("i2 为 " +i2 +",i3 为 " +i3 + ",i5 为 " +i5 );
System.out.println("i为 "+i+" , list.get(i2) "+ list.get(i2) +" , list.get(i3) = "+ list.get(i3) +" , list.get(i5) = "+ list.get(i5) );
min = Math.min(m2,Math.min(m3,m5));
if(min == m2){
i2++;
}
if(min == m3){
i3++;
}
if(min == m5){
i5++;
}
System.out.println("i为 "+i+" , m2 = "+ m2 +" , m3 = "+ m3 +" , m5 = "+ m5 +" , min = "+min);
System.out.println("i2 为 " +i2 +",i3 为 " +i3 + ",i5 为 " +i5 );
list.add(min);
System.out.println(list);
System.out.println( );
System.out.println( );
}
return list.get(list.size()-1);
}
}
运算的结果如下:
i为 1 , m2 = 2 , m3 = 3 , m5 = 5
i2 为 0,i3 为 0,i5 为 0
i为 1 , list.get(i2) 1 , list.get(i3) = 1 , list.get(i5) = 1
i为 1 , m2 = 2 , m3 = 3 , m5 = 5 , min = 2
i2 为 1,i3 为 0,i5 为 0
[1, 2]
i为 2 , m2 = 4 , m3 = 3 , m5 = 5
i2 为 1,i3 为 0,i5 为 0
i为 2 , list.get(i2) 2 , list.get(i3) = 1 , list.get(i5) = 1
i为 2 , m2 = 4 , m3 = 3 , m5 = 5 , min = 3
i2 为 1,i3 为 1,i5 为 0
[1, 2, 3]
i为 3 , m2 = 4 , m3 = 6 , m5 = 5
i2 为 1,i3 为 1,i5 为 0
i为 3 , list.get(i2) 2 , list.get(i3) = 2 , list.get(i5) = 1
i为 3 , m2 = 4 , m3 = 6 , m5 = 5 , min = 4
i2 为 2,i3 为 1,i5 为 0
[1, 2, 3, 4]
i为 4 , m2 = 6 , m3 = 6 , m5 = 5
i2 为 2,i3 为 1,i5 为 0
i为 4 , list.get(i2) 3 , list.get(i3) = 2 , list.get(i5) = 1
i为 4 , m2 = 6 , m3 = 6 , m5 = 5 , min = 5
i2 为 2,i3 为 1,i5 为 1
[1, 2, 3, 4, 5]
i为 5 , m2 = 6 , m3 = 6 , m5 = 10
i2 为 2,i3 为 1,i5 为 1
i为 5 , list.get(i2) 3 , list.get(i3) = 2 , list.get(i5) = 2
i为 5 , m2 = 6 , m3 = 6 , m5 = 10 , min = 6
i2 为 3,i3 为 2,i5 为 1
[1, 2, 3, 4, 5, 6]
i为 6 , m2 = 8 , m3 = 9 , m5 = 10
i2 为 3,i3 为 2,i5 为 1
i为 6 , list.get(i2) 4 , list.get(i3) = 3 , list.get(i5) = 2
i为 6 , m2 = 8 , m3 = 9 , m5 = 10 , min = 8
i2 为 4,i3 为 2,i5 为 1
[1, 2, 3, 4, 5, 6, 8]
i为 7 , m2 = 10 , m3 = 9 , m5 = 10
i2 为 4,i3 为 2,i5 为 1
i为 7 , list.get(i2) 5 , list.get(i3) = 3 , list.get(i5) = 2
i为 7 , m2 = 10 , m3 = 9 , m5 = 10 , min = 9
i2 为 4,i3 为 3,i5 为 1
[1, 2, 3, 4, 5, 6, 8, 9]
i为 8 , m2 = 10 , m3 = 12 , m5 = 10
i2 为 4,i3 为 3,i5 为 1
i为 8 , list.get(i2) 5 , list.get(i3) = 4 , list.get(i5) = 2
i为 8 , m2 = 10 , m3 = 12 , m5 = 10 , min = 10
i2 为 5,i3 为 3,i5 为 2
[1, 2, 3, 4, 5, 6, 8, 9, 10]
i为 9 , m2 = 12 , m3 = 12 , m5 = 15
i2 为 5,i3 为 3,i5 为 2
i为 9 , list.get(i2) 6 , list.get(i3) = 4 , list.get(i5) = 3
i为 9 , m2 = 12 , m3 = 12 , m5 = 15 , min = 12
i2 为 6,i3 为 4,i5 为 2
[1, 2, 3, 4, 5, 6, 8, 9, 10, 12]
12
总结:
* 这种思路的关键在于怎样确保数组里面的丑数是排好序的。
* 我们假设数组中已经有若干个丑数,排好序后存在数组中。
* 我们把现有的最大丑数记做M。
* 现在我们来生成下一个丑数,该丑数肯定是前面某一个丑数乘以2、3或者5的结果。
* 我们首先考虑把已有的每个丑数乘以2。在乘以2的时候,能得到若干个结果小于或等于M的。
* 由于我们是按照顺序生成的,小于或者等于M肯定已经在数组中了,我们不需再次考虑;
* 我们还会得到若干个大于M的结果,但我们只需要第一个大于M的结果,因为我们希望丑数是按从小到大顺序生成的,其他更大的结果我们以后再说。
* 我们把得到的第一个乘以2后大于M的结果,记为M2。
* 同样我们把已有的每一个丑数乘以3和5,能得到第一个大于M的结果M3和M5。
* 那么下一个丑数应该是M2、M3和M5三个数的最小者。
*
*
*前面我们分析的时候,提到把已有的每个丑数分别都乘以2、3和5,事实上是不需要的,
*因为已有的丑数是按顺序存在数组中的。对乘以2而言,肯定存在某一个丑数T2,
*排在它之前的每一个丑数乘以2得到的结果都会小于已有最大的丑数,在它之后的每一个丑数乘以2得到的结果都会太大。
*我们只需要记下这个丑数的位置,同时每次生成新的丑数的时候,去更新这个T2。
* 对乘以3和5而言,存在着同样的T3和T5。
*
* 大智小结: 1. 任何一个丑数 都是 2、3、5的倍数
* 2. 获得 一个丑数需要比较 三个数字 m2 ,m3,m5 。而m2 ,一定是上一个 m2 的两倍关系。