您的位置:首页 > Web前端

剑指Offer(java版):丑数

2016-08-01 00:00 537 查看
题目:丑数
* 我们把只包含因子2,3,5的数称为丑数(Ugly Number).
* 求按从小到大的顺序的第1500个丑数。

* 例如6,8都是丑数,但14不是,因为它含有因子7.习惯上我们把1当作第一个丑数

方法一:逐个判断每个整数是不是丑数的解法,直观但不够高效:

所谓一个数m是另一个数n的因子,是指n能被m整除,也就是说 n%m==0.根据丑数的定义,丑数只能被2,3,5整除。也就是说如果一个数能被2整除,我们把它连续除以2;如果能被3整除,就连续除以3;如果能被 5整除,就除以5.如果最后我们得到的是1,那么这个数就是丑数,否则不是。

接下来,我们只需要按照顺序判断每个整数是不是丑数,

适用Java代码实现:

package cglib;

public class jiekou {

public boolean isUgly(int number){
//System.out.println("进入isUgly");
while(number % 2 == 0)
{
//System.out.println("number模2为0="+number);
number/=2;
//System.out.println("number整除2后="+number);

}
while(number % 3 == 0)
number /=3;
while(number % 5 == 0)
number /=5;

//System.out.println("最后number="+number);
return (number ==1)? true:false;
}
public int getUglyNumber(int index){

if(index <= 0)
return 0;
int number = 0;
int uglyFound = 0;
// System.out.println("index="+index);
while(uglyFound < index){
//System.out.println("uglyFound="+uglyFound);
number++;
//System.out.println("number="+number);
if(isUgly(number)){ //从1开始
++uglyFound;
//System.out.println("++uglyFound="+uglyFound);
}
}
return number;
}
public static void main(String[] args){
int index = 150;
jiekou test = new jiekou();
System.out.println(test.getUglyNumber(index));
}
}

我们只需要在函数getUglyNumber 中传入参数1500,就能得到第1500个丑数。该算法非常直观,代码也非常简介,但最大的问题是每个整数都需要计算。即使一个数字不是丑数,我们还是需 要对它做求余和除法操作。因此该算法的时间效率不是很高,面试官也不会就此满足,还会提示我们有更高效的算法。

方法二:创建数组保存已经找到的丑数,用空间换时间的解法:

前面的算法之所以效率低,很大程度上是因为不管一个数是不是丑数我们 对它都要作计算。接下来我们试着找到一种只要计算丑数的方法,而不在非丑数的整数上花费时间。根据丑数的定义,丑数应该是另一个丑数乘以2,3,5的结 果。因此我们可以创建一个数组,里面的数字是排序好的丑数,每一个丑数都是前面的丑数乘以2,3,5得到的。

这种思路的关键在于怎样确定数组里面的丑数是排序好的。假设数组中已 经有若干个丑数排好后存放在数组中,并且把已有的最大的丑数记作M,我们接下来分析如何生成下一个丑数。该丑数肯定是前面某个丑数乘以2,3,5的结果。 所以我们首先考虑把已有的每个丑数乘以2.在乘以2的时候,能得到若干个小于或等于M的结果。由于是按照顺序生成的,小于或者等于M肯定已经在数组中了, 我们不需要再次考虑;还会得到若干个大于M的结果,但我们只需要第一个大于M的结果,因为我们希望丑数是指按从小到大的顺序生成的,其他更大的结果以后再 说。我们把得到的第一个乘以2后大于M的结果即为M2.同样,我们把已有的每一个丑数乘以3,5,能得到第一个大于M的结果M3和M5.那么下一个丑数应 该是M2,M3,M5。这3个数的最小者。

前面分析的时候,提到把已有的每个丑数分别都乘以2,3,5.事实上 这不是必须的,因为已有的丑数都是按顺序存放在数组中的。对乘以2而言,肯定存在某一个丑数T2,排在它之前的每一个丑数乘以2得到的结果都会小于已有的 最大丑数,在它之后的每一个丑数乘以2得到的结果都会太大。我们只需记下这个丑数的位置,同时每次生成新的丑数的时候,去更新这个T2.对乘以3和5而 言,也存在这同样的T3和T5.

Java代码实现:

package cglib;

public class jiekou {

/*****
*
* 程序中采用double的原因是:因为int型表示数据范围有限倒置数据溢出,unsigned long int 仍然溢出三者的表示的数据范围如下:
int max --> 2147483647 在第1692个丑数越界
unsigned long int max --> 4294967295 在第1849个丑数越界
double max --> 1.79769e+308
* @param a
* @param b
* @param c
* @return
*/
private double findTheMinNumber(double a,double b,double c){
//找到a,b之间最小的
double temp = a < b ? a : b;
//找到abc之间最小的返回
return temp < c ? temp : c;
}

private double findUgly(int n){
if(n<0)
return 0;
double[] ugly = new double
;
ugly[0] = 1;
int index = 1;
int index2 = 0;
int index3 = 0;
int index5 = 0;
while(index < n){
double var = this.findTheMinNumber(ugly[index2]*2, ugly[index3]*3, ugly[index5]*5);//找到这三个数的最小值
if (var == ugly[index2]*2) {
++index2;
}
if (var == ugly[index3]*3) {
++index3;
}
if (var == ugly[index5]*5) {
++index5;
}
System.out.println("var="+var);
ugly[index++] = var;//将比较后最小的丑数放到数组里
System.out.println("index="+index);
}

//循环输出所有的丑数
for (int i = 1; i <= ugly.length; i++) {

System.out.println("the "+i+" th ugly number is "+ugly[i-1]);
}
return ugly[index-1];//返回的是最后一个丑数
}

public static void main(String[] args) {
jiekou find = new jiekou();
find.findUgly(2012);
}
}

输出:

var=3.9366E9
index=1829
var=3.955078125E9
index=1830
var=3.981312E9
index=1831
var=3.9858075E9
index=1832
var=4.0E9
index=1833
var=4.02653184E9
index=1834
var=4.0310784E9
index=1835
var=4.05E9
index=1836
var=4.076863488E9
index=1837
var=4.08146688E9
index=1838
var=4.096E9
index=1839
var=4.100625E9
index=1840
var=4.132485216E9
index=1841
var=4.1472E9
index=1842
var=4.194304E9
index=1843
var=4.19904E9
index=1844
var=4.21875E9
index=1845
var=4.2467328E9
index=1846
var=4.251528E9
index=1847
var=4.271484375E9
index=1848
var=4.294967296E9
index=1849
var=4.29981696E9
index=1850
var=4.3046721E9
index=1851
var=4.32E9
index=1852
var=4.353564672E9
index=1853
var=4.374E9
index=1854
var=4.39453125E9
index=1855
var=4.42368E9
index=1856
var=4.428675E9
index=1857
var=4.478976E9
index=1858
var=4.5E9
index=1859
var=4.52984832E9
index=1860
var=4.5349632E9
index=1861
var=4.55625E9
index=1862
var=4.586471424E9
index=1863
var=4.59165024E9
index=1864
var=4.608E9
index=1865
var=4.613203125E9
index=1866
var=4.649045868E9
index=1867
var=4.6656E9
index=1868
var=4.6875E9
index=1869
var=4.718592E9
index=1870
var=4.72392E9
index=1871
var=4.74609375E9
index=1872
var=4.7775744E9
index=1873
var=4.782969E9
index=1874
var=4.8E9
index=1875
var=4.831838208E9
index=1876
var=4.83729408E9
index=1877
var=4.86E9
index=1878
var=4.8828125E9
index=1879
var=4.897760256E9
index=1880
var=4.9152E9
index=1881
var=4.92075E9
index=1882
var=4.97664E9
index=1883
var=4.982259375E9
index=1884
var=5.0E9
index=1885
var=5.0331648E9
index=1886
var=5.038848E9

。。。。。。。。

the 149 th ugly number is 5760.0
the 150 th ugly number is 5832.0
the 151 th ugly number is 6000.0
the 152 th ugly number is 6075.0
the 153 th ugly number is 6144.0
the 154 th ugly number is 6250.0
the 155 th ugly number is 6400.0
the 156 th ugly number is 6480.0
the 157 th ugly number is 6561.0
the 158 th ugly number is 6750.0
the 159 th ugly number is 6912.0
the 160 th ugly number is 7200.0
the 161 th ugly number is 7290.0
the 162 th ugly number is 7500.0
the 163 th ugly number is 7680.0
the 164 th ugly number is 7776.0
the 165 th ugly number is 8000.0
the 166 th ugly number is 8100.0
the 167 th ugly number is 8192.0
the 168 th ugly number is 8640.0
the 169 th ugly number is 8748.0
the 170 th ugly number is 9000.0
the 171 th ugly number is 9216.0
the 172 th ugly number is 9375.0
the 173 th ugly number is 9600.0
the 174 th ugly number is 9720.0

。。。。。。。。。。。。。。

the 2006 th ugly number is 8.264970432E9
the 2007 th ugly number is 8.2944E9
the 2008 th ugly number is 8.303765625E9
the 2009 th ugly number is 8.388608E9
the 2010 th ugly number is 8.39808E9
the 2011 th ugly number is 8.4375E9
the 2012 th ugly number is 8.4934656E9

和第一种思路相比,第二种思路不需要在非丑数的整数上做任何计算,因此时 间效率有明显上升。但也需要指出,第二种算法由于需要保存已经生成的丑数,因此需要一个数组,从而增加了空间消耗。如果是求第1500个丑数,将创建一个 能容纳1500个丑数的数组,这个数组占内存6KB。而第一种思路没有这样的内存开销。总的来说,第二种思路相当于用较少的空间换取了时间效率上的提升。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: