丑数
2015-08-28
次访问
剑指offer面试题系列
题目
我们把只包含因子2、3、5的数成为丑数,求按从小到大的顺序的第 1500 个丑数。习惯上我们把 1 当成第一个丑数。
思路
方法一:
让循环一直继续,直到找到第 1500 个丑数。这种方法的思路就是对于从 1 开始的每一个数都检验是不是丑数。所以时间耗费较大。
可以看出来,这种方法耗费了将近2分钟的时间。
方法二:
在第一种方法的基础上进行了改进,改善时间复杂度除了改变本身算法之外,另外一个最常用的方法就是以空间换取时间。
用一个数组保存已经遍历到的丑数。
解决此问题的关键所在就是利用到了一条规律:丑数的2、3、5倍还是丑数。
假设数组 a 中存放的都是之前遍历到的丑数,且已经排好序,最大值为 M。
可以想到如果给这个数组的每一个元素都分别乘以2、3、5,则产生的又会是新的丑数,但是有一点要注意:如果新的丑数小于M,则舍弃,因为这种丑数必然已经在 a数组中了。
怎么样来保证数组一直是排好序的呢?
通过比较来得出在 M 后面的下一个最小的丑数。就像代码中所示的那样,通过比较数组中的数字乘以 2、3、5 得到的结果,来得到最小的数字,然后排到数组 a 中。
以此类推,直到数组a的元素个数已经达到 1500 个。
比如 数组a中存放 1,2两个元素, M = 2。
乘以 2: 2,4 (2 <= M,意味着数组a中已经有了,舍去)
乘以 3:3,6
乘以5: 5,10
通过比较得出下一个元素是:3.
这种以空间来换取时间的方式大幅减小了程序运行所耗费的时间。
不到0.1秒。性能得到了很大的改善。
而空间只占用了 6KB.
代码
code1
package com.ans;
//find ugly numnber
public class Solution2 {
// 输入序号,表示要寻找第几个丑数
public static int findUglyNum(int seq) {
int i = 2;
int count = 1;
int retVal = 1;
while (true) {
if (FenJie(i)) { // 前提条件
// 测试还有没有其他的因子
count++;
System.out.println("丑数:" + i + ",序号: " + count);
if (count == seq) {
retVal = i;
break;
}
i++;
} else {
i++;
}
}
return retVal;
}
public static boolean FenJie(int num) {
boolean flag = false;
while (num % 2 == 0) {
num /= 2;
}
while (num % 3 == 0) {
num /= 3;
}
while (num % 5 == 0) {
num /= 5;
}
if (num == 1) {
flag = true;
}
return flag;
}
public static void main(String[] args) {
int N = 1500;
System.out.println("程序开始运行");
long startTime = System.currentTimeMillis();
//System.out.println("第" + N + "个丑数是:" + findUglyNum(N));
System.out.println("第" + N + "个丑数是:" + getUglyNum(N));
long endTime = System.currentTimeMillis(); // 获取结束时间
System.out.println("程序运行时间: " + (endTime - startTime) + "ms");
}
}
code2
public static int getUglyNum(int seq) {
int[] arr = new int[seq];
arr[0] = 1;
int M = 1;
int M2 = 1;
int M3 = 1;
int M5 = 1;
int index = 1;
while (index < seq) {
for (int i = 0; i < index; i++) {
M2 = 2 * arr[i];
if (M2 > M) {
System.out.println("M2 " + M2);
break;
}
}
for (int i = 0; i < index; i++) {
M3 = 3 * arr[i];
if (M3 > M) {
System.out.println("M3 " + M3);
break;
}
}
for (int i = 0; i < index; i++) {
M5 = 5 * arr[i];
if (M5 > M) {
System.out.println("M5 " + M5);
break;
}
}
M = getMinNum(M2, M3, M5);
arr[index++] = M;
System.out.println("M value: " + M);
}
return arr[index - 1];
}
// 找出三个数中的最小数
public static int getMinNum(int num1, int num2, int num3) {
int min = num1 < num2 ? num1 : num2;
min = min < num3 ? min : num3;
return min;
}
总结
当你实在没有办法通过改进算法来减小时间复杂度,不妨试试“以空间来换取时间”这种方式。