丑数--中包含因子2、 3 、 5 的数称为丑数

来源:互联网 发布:mac文档在哪里 编辑:程序博客网 时间:2024/04/30 16:03

丑数:只包含因子 2、 3、 5 的数称为丑数,例如 2 、 3 、 4 、 5 都是都是丑数,但是 7 、 14  不是丑数。现在求从小到大的第 num 个丑数,我们把 1 作为第一个丑数。————  源自《剑指offer》


思路:最简单暴力的方法就是从 1 开始枚举,直接枚举到 第 num 个丑数,这是可行的,但是非常的耗时,比如第 1500 个丑数是  859963392,那么我们需要从 1 开始枚举到 859963392 ,这所需花费的时间让人惆怅啊 。

当然了,最简单暴力的往往都不是最好的,也不是面试官想看到的,毕竟这么简答暴力的东西,谁都能想到啊,并没什么技术含量。

所以我们需要更好的方法。因为丑数的因子只有 2    3   或者  5,所以丑数必定是 某个丑数 k 的 2倍、3倍、或者 5倍。

这样我们就不需要从 1 开始枚举了到第 num 个丑数 target,我们可以根据得到丑数来推导出下一个丑数,这样大大节省了时间,接下来就是该如何去找到 这个 丑数 k 。

我们需要一个辅助数组  arr 来存储我们找到的丑数,最笨的方法当然是每次从数组的第一个开始找 k ,使得它的 2倍 3倍 或者 5倍 大于当前的数组最大值。

当然 k 的值不一定是唯一的,比如我们现在找第 4 丑数.我们从 1 开始,此时数组中最大的数 max = 1 :

max = 1 : 2*1 = 2、 3*1 = 3 、 5*1 = 5 ,因为 2 最小,所以第二个丑数arr[ 1 ] 是 2 .

max = 2: 2*1 = 2 <= 2 ,  2*2 = 4、 3*1 = 3 ,5*1 = 5 ,最小的数 是 3 ,所以第三个丑数 arr[ 2 ]是 3 .

max = 3:2*1 = 2 < 3 , 2 *2 = 4, 3*1 = 3 <= 3 , 3*2 = 6 , 5*1 = 5,最小的数是 4 ,所以第四个丑数 arr[ 3 ] 是 4.

根据上面的例子,可以看出,我们不必每次都从数组的第一个丑数开始,而是分别设置 3 个变量来存储数组的下标,然后动态的去改变下标。分析到此,具体的代码如下:

import java.util.Scanner;/** * 我们把值包含因子  2、 3、 5 的数称作丑数,求第 num 个丑数,我们将 1 看做第一个丑数。 * 例如 4  、    6  、   8 都是丑数,但是 14 不是 因为14 包含因子 7 * @author luzi * */public class uglyNumber {public static void main(String[] args) {// TODO Auto-generated method stubScanner scan = new Scanner(System.in);while(scan.hasNext()){int num = scan.nextInt();long startTime = System.currentTimeMillis();System.out.println(UglyNumber2(num));long endTime = System.currentTimeMillis();System.out.println("运行时间 : " + (endTime - startTime) + "ms");startTime = System.currentTimeMillis();System.out.println(UglyNumber(num));endTime = System.currentTimeMillis();System.out.println("运行时间 : " + (endTime - startTime) + "ms");}}//用暴力的方法,从 1 开始枚举自然数 到 第num 个丑数 targetprivate static int UglyNumber(int num) {// TODO Auto-generated method stubif(num <= 0)return 0;int n = 0;int count = 0;while(count < num){n++;if(isUgly(n))count++;}return n;}public static 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;}//根据丑数的规律,用一个辅助数组,大大减小时间复杂度public static int UglyNumber2(int num){if(num <= 0)return 0;int[] arr = new int[num];arr[0] = 1;int temp2 = 0;int temp3 = 0;int temp5 = 0;int nextNum = 1;while(nextNum < num){int min = Math.min(Math.min(arr[temp2]*2, arr[temp3]*3), arr[temp5]*5);arr[nextNum] = min;while(arr[temp2]*2 <= min)temp2++;while(arr[temp3]*3 <= min)temp3++;while(arr[temp5]*5 <= min)temp5++;nextNum++;}return arr[num - 1];}}


以下是两种方法实际运行时间对比:


可以看出,当丑数比较小的时候两种方法所耗费时间几乎一样,但随着 num 的增加,耗费时间也在增加,当 num 大于1000 时耗费的时间差距越发明显


原创粉丝点击