Write a program to find the n
-th ugly number.
Ugly numbers are positive numbers whose prime factors only include 2, 3, 5
. For example, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12
is the sequence of the first 10
ugly numbers.
Note that 1
is typically treated as an ugly number.
Hint:
- The naive approach is to call
isUgly
for every number until you reach the nth one. Most numbers are not ugly. Try to focus your effort on generating only the ugly ones. - An ugly number must be multiplied by either 2, 3, or 5 from a smaller ugly number.
- The key is how to maintain the order of the ugly numbers. Try a similar approach of merging from three sorted lists: L1, L2, and L3.
- Assume you have Uk, the kth ugly number. Then Uk+1 must be Min(L1 * 2, L2 * 3, L3 * 5).
【题目分析】
1. ugly数
ugly数进行质因数分解之后只包含2,3,5,1默认为第一个ugly数。
2. 题目要求求出第n个ugly数
给出的提示包含一个重要的信息:即较大的ugly数都是由较小的ugly数乘以2或3或5得到的,而且是递增的。
【思路】
我们举一个简单的例子来分析一下
1. 初始第一个ugly数是1. 我们来求第二个ugly数。
计算:1*2,1*3,1*5 结果中2是最小的,因此第二个ugly数是2
2. 计算第三个ugly数
2与已有的ugly数逐个相乘,找到第一个比2大的数——4
3与已有的ugly数逐个相乘,找到第一个比2大的数——3
3与已有的ugly数逐个相乘,找到第一个比2大的数——5
取结果中最小的——3,因此第三个ugly数是3
3. 计算第四个ugly数
2与已有的ugly数逐个相乘,找到第一个比2大的数——6
3与已有的ugly数逐个相乘,找到第一个比2大的数——6
3与已有的ugly数逐个相乘,找到第一个比2大的数——5
取结果中最小的——5,因此第三个ugly数是5
以此类推:
在这个过程中我们可以为2,3,5每个数维持一个指针,指向上次相乘结果大于最后一个ugly数的那个数。这样可以避免冗余计算。
【java代码】
1 public class Solution { 2 public static int nthUglyNumber(int n) { 3 int[] uglynum = new int[n+1]; 4 uglynum[1] = 1; 5 6 int point2 = 1, point3 = 1, point5 = 1; 7 for(int i = 2; i <= n; i++){ 8 int cpoint2, cpoint3, cpoint5; 9 int num2 = 0, num3 = 0, num5 = 0; 10 for(cpoint2 = point2; cpoint2 < i; cpoint2++){ //找到乘以2第一个大于当前最后一个ugly数的数 11 if(uglynum[cpoint2]*2 > uglynum[i-1]){ 12 num2 = uglynum[cpoint2]*2; 13 break; 14 } 15 } 16 for(cpoint3 = point3; cpoint3 < i; cpoint3++){ //找到乘以3第一个大于当前最后一个ugly数的数 17 if(uglynum[cpoint3]*3 > uglynum[i-1]){ 18 num3 = uglynum[cpoint3]*3; 19 break; 20 } 21 } 22 for(cpoint5 = point5; cpoint5 < i; cpoint5++){ //找到乘以5第一个大于当前最后一个ugly数的数 23 if(uglynum[cpoint5]*5 > uglynum[i-1]){ 24 num5 = uglynum[cpoint5]*5; 25 break; 26 } 27 } 28 if(num2 <= num3 && num2 <= num5){ 29 uglynum[i] = num2; 30 point2 = cpoint2+1; 31 point3 = cpoint3; 32 point5 = cpoint5; 33 } 34 else if(num3 <= num2 && num3 <= num5){ 35 uglynum[i] = num3; 36 point3 = cpoint3+1; 37 point2 = cpoint2; 38 point5 = cpoint5; 39 } 40 else{ 41 uglynum[i] = num5; 42 point5 = cpoint5+1; 43 point3 = cpoint3; 44 point2 = cpoint2; 45 } 46 } 47 return uglynum[n]; 48 } 49 }
【代码优化】
1 public class Solution { 2 public static int nthUglyNumber(int n) { 3 int[] uglynum = new int[n+1]; 4 uglynum[1] = 1; 5 6 int pointer2 = 1, pointer3 = 1, pointer5 = 1; 7 for(int i = 2; i <= n; i++){ 8 int min = Math.min(Math.min(uglynum[pointer2]*2, uglynum[pointer3]*3),uglynum[pointer5]*5); 9 if(min == uglynum[pointer2]*2) pointer2++; 10 if(min == uglynum[pointer3]*3) pointer3++; 11 if(min == uglynum[pointer5]*5) pointer5++; 12 uglynum[i] = min; 13 } 14 return uglynum[n]; 15 } 16 }
在参考别人代码的时候,发现自己的思路和别人的思路实际上是类似的,但是在代码上的差距竟然这么大!这不仅表现在代码上,也表现在时间复杂度上!