编程练习
题目:把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
思路:这道题看似简单,实际要通过很难。首先想到的方法是先实现一个判断给定整数是否是丑数的方法,然后对从1到N的所有整数调用该方法,得到N以内的所有丑数,最后根据输入的参数index找出第index个即可。
java代码如下:
1 | import java.util.ArrayList; |
但是实际实现中发现,丑数的增长速度远远大于其个数的增长,如果你想要得到第1500个丑数,你要获取到的丑数list中的最大值N至少得为859963392,而对这么大的数做for循环统计N以内的丑数,很耗费时间,我一开始就是用的这个方法,提交时提示超时,本地跑了十几秒才跑出来第1500个丑数859963392。毕竟是自己想出来的方法,不忍心就这么抛弃,所以想着改进算法,后来看到别人写的一个判断丑数的方法比我的要效率高,所以拿过来用了一下,该方法是我见过最简单易懂且效率高的判断丑数的方法,下面贴出来记录一下。
1 | /** |
通过改进丑数判断以及查找算法,可以将输入1500的结果优化到十秒左右完成,还是太慢,效率太低。遂放弃该方法,开始考虑丑数生成的规律,从规律入手来尝试新的算法。先写出10以内的丑数,即1,2,3,4,5,6,8,9,10;可见除了1以外,每个数字都可以写成前几个数字x2,x3,x5的形式。比如2=1x2,3=1x3,4=2x2,5=1x5,6=2x3,8=4x2,9=3x3,10=2x5。可以估计后面的所有丑数都可以由前边的丑数乘以丑数因子2,3,5得到,所以考虑如下算法:
- 首先确定第一个丑数为1,然后计算1x2,1x3,1x5的值,取出其中大于1的最小的数作为下一个丑数,此时得到的是2;
- 完成第一步后,此时的丑数序列为1,2。分别计算1x2=2,1x3=3,1x5=5,2x2=4,2x3=6,2x5=10。再挑出其中大于2的值为3,5,4,6,10,取最小值3作为下一个丑数。
- 以此类推……
- 要计算第i个丑数,分别计算前i-1个丑数乘以2,3,5因子中的大于第i-1个丑数的最小值即可。
java代码如下:
1 | import java.util.ArrayList; |
此代码可以很快的计算出n比较大时的第n个丑数,在牛客网上通过的时间为48ms,因为它生成的每一个数都是丑数,省去了很多不必要的循环次数,至于为什么我定义的存放丑数的int数组大小为1691,是因为我经过测试后发现第1691个丑数的值为2125764000,而java中int型的可以表示的整数的最大值为2147483648 ,经过测试第1692个丑数就会超出这个范围,因为题目要求返回类型为int型,所以设置1691大小的数组完全够用了,避免了存储空间的浪费。