题目描述
把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
思路详解:
如果p是丑数,那么p=2^x * 3^y * 5^z,那么只要赋予x,y,z不同的值就能得到不同的丑数。
对于任何丑数p:如果p小于q, 那么2*p<2*q,3*p<3*q,5*p<5*q,由于1是最小的丑数,那么从1开始,把2*1,3*1,5*1,进行比较,得出最小的就是1的下一个丑数,也就是2*1, 这个时候,多了一个丑数‘2’,也就又多了3个可以比较的丑数,2*2,3*2,5*2,这时就把之前‘1’生成的丑数和‘2’生成的丑数加进来也就是(3*1,5*1,2*2,3*2,5*2)进行比较,找出最小的。。。如此循环下去就会发现,每次选进来一个丑数,该丑数又会生成3个新的丑数进行比较。
上面的暴力方法也应该能解决,下面说一个O(n)的算法:
在上面既然有p小于q, 那么2*p<2*q,那么“我”在前面比你小的数都没被选上,你后面生成新的丑数一定比“我”大吧,那么你乘2生成的丑数一定比我乘2的大吧,那么在我选上之后你才有机会选上。其实每次我们只用比较3个数:用于乘2的最小的数、用于乘3的最小的数,用于乘5的最小的数。也就是比较(2*x , 3*y, 5*z) ,x>=y>=z的
《2》
每个位置上的丑数都是某个丑数乘上2或3或5的结果,看如下示例:
第1个丑数是 1,
第2个丑数是 1 * 2 = 2, 第1个丑数*2
第3个丑数是 1 * 3 = 3, 第1个丑数*3
第4个丑数是 2 * 2 = 4, 第2个丑数*2
第5个丑数是 1 * 5 = 5, 第1个丑数*5
第6个丑数是 2 * 3 = 6, 第2个丑数*3
、、、、、、、、、、、、、、、
我们以p2,p3,p5分别记录为2、3、5出现的位置,即用来表示丑数数组a中哪个下标对应的元素乘以2,3,5,
初始都为0,设丑数数组为a,第1个丑数a[0]=1,
第2个丑数为min(a[p2]*2 , a[p3]*3, a[p5]*5),然后最小值为a[p2]*2=a[0]*2,所以p2++后,p2=1,这样下一次就是计算第2个丑数乘以2,即a[p2]=a[1]*2 =2*2
从而保证了丑数序列的不断递增,直到找到第index丑数。
public class Solution
{
public int GetUglyNumber_Solution(int index)
{
//边界判断
if (index ==0)
return 0;
//开辟数组,存放丑数
int[] a = new int[index];
a[0] = 1;
//p2,p3,p5用来表示丑数数组a中哪个下标的元素乘以2,3,5,记录的是数组中丑数的下标
int p2 = 0, p3 = 0, p5 = 0;
for (int i = 1; i < index; i++)
{
a[i] = Math.min(a[p2] * 2, Math.min(a[p3] * 3, a[p5] * 5));
if( a[i]==a[p2] * 2 )p2++; //如a[0]*2=2最小,则p2++,p2=1,下次比较a[1]*2,a[0]*3,a[0]*5
if (a[i]==a[p3] * 3) p3++;
if (a[i]==a[p5] * 5) p5++;
}
return a[index - 1];
}
}