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 first10
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 n<sup style="box-sizing: border-box; position: relative; font-size: 12px; line-height: 0; vertical-align: baseline; top: -0.5em;">th 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: L<sub style="box-sizing: border-box; position: relative; font-size: 12px; line-height: 0; vertical-align: baseline; bottom: -0.25em;">1, L<sub style="box-sizing: border-box; position: relative; font-size: 12px; line-height: 0; vertical-align: baseline; bottom: -0.25em;">2, and L<sub style="box-sizing: border-box; position: relative; font-size: 12px; line-height: 0; vertical-align: baseline; bottom: -0.25em;">3.
- Assume you have U<sub style="box-sizing: border-box; position: relative; font-size: 12px; line-height: 0; vertical-align: baseline; bottom: -0.25em;">k, the k<sup style="box-sizing: border-box; position: relative; font-size: 12px; line-height: 0; vertical-align: baseline; top: -0.5em;">th ugly number. Then U<sub style="box-sizing: border-box; position: relative; font-size: 12px; line-height: 0; vertical-align: baseline; bottom: -0.25em;">k+1 must be Min(L<sub style="box-sizing: border-box; position: relative; font-size: 12px; line-height: 0; vertical-align: baseline; bottom: -0.25em;">1 * 2, L<sub style="box-sizing: border-box; position: relative; font-size: 12px; line-height: 0; vertical-align: baseline; bottom: -0.25em;">2 * 3, L<sub style="box-sizing: border-box; position: relative; font-size: 12px; line-height: 0; vertical-align: baseline; bottom: -0.25em;">3 * 5).
題意就不翻譯了,意思就是把丑數(shù)從小到大排列闪唆,輸出制定位置的那個(gè)丑數(shù)是多少盅粪,按照上一題的那種樸素判斷當(dāng)然是行得通的,只要把1~n的數(shù)全部都驗(yàn)證一遍悄蕾,自然可以找到第n個(gè)票顾,但是認(rèn)真想想,一旦n大了之后帆调,其實(shí)丑數(shù)的個(gè)數(shù)是不多的奠骄,也就是說驗(yàn)證成功的次數(shù)遠(yuǎn)遠(yuǎn)小于失敗的,所以用樸素的驗(yàn)證方式贷帮,顯然是相當(dāng)浪費(fèi)時(shí)間(事實(shí)上也是)戚揭,所以我們的思路就是想到如何一個(gè)一個(gè)計(jì)算出丑數(shù)诱告,找到其中的規(guī)律撵枢,主動(dòng)去計(jì)算丑數(shù)而不是被動(dòng)得驗(yàn)證是不是丑數(shù)
My Solution
(Java) Version 1 Time: 131ms:
計(jì)算丑數(shù)的原理當(dāng)然不難,不要想到這一點(diǎn)還是花了不少功夫精居,首先每個(gè)丑數(shù)都是由2,3,5相乘得來的锄禽,而且每一個(gè)相同乘數(shù)的個(gè)數(shù)還不定,比如有5個(gè)5靴姿,3個(gè)1之類的沃但,所以只能從小到大一個(gè)一個(gè)算出來,然后我們又發(fā)現(xiàn)了每個(gè)丑數(shù)其實(shí)都是由前面小的丑數(shù)乘以2,3,5得來的佛吓,那就很明顯了宵晚,我們只要把前面的丑數(shù)都乘以2,3,5,然后結(jié)果里面最小的那個(gè)就一定是下一個(gè)丑數(shù)了
public class Solution {
public int nthUglyNumber(int n) {
int[] nums = new int[n];
nums[0]=1;
int max2=0,max3=0,max5=0,index;
for(int i=0;i<n-1;i++){
index=0;
while(max2<=nums[i]){
max2=nums[index]*2;
index++;
}
index=0;
while(max3<=nums[i]){
max3=nums[index]*3;
index++;
}
index=0;
while(max5<=nums[i]){
max5=nums[index]*5;
index++;
}
nums[i+1]=max2<max3?max2<max5?max2:max5<max3?max5:max3:max3<max5?max3:max5;
}
return nums[n-1];
}
}
(Java) Version 2 Time: 46ms (By luke.wang.7509):
這個(gè)解法的生成形式和上一個(gè)并無不同维雇,不過上一個(gè)解法每一次都要從第一個(gè)丑數(shù)開始乘顯然是沒有必要的淤刃,因?yàn)橛泻芏嘀貜?fù),這里就避免了這個(gè)問題
public class Solution {
public int nthUglyNumber(int n) {
if(n<=0) return 0;
int a=0,b=0,c=0;
List<Integer> table = new ArrayList<Integer>();
table.add(1);
while(table.size()<n)
{
int next_val = Math.min(table.get(a)*2,Math.min(table.get(b)*3,table.get(c)*5));
table.add(next_val);
if(table.get(a)*2==next_val) a++;
if(table.get(b)*3==next_val) b++;
if(table.get(c)*5==next_val) c++;
}
return table.get(table.size()-1);
}
}
(Java) Version 3 Time: 4ms (By TheKingRingReal):
DFS是深度優(yōu)先的搜索算法吱型,這里我還沒能理解逸贾,不過從結(jié)果上看,已經(jīng)很顯然了……4ms快到媽都不認(rèn)得是誰
作者的解釋:
we can get a tree like this which contains all the ugly number ;just use three point to dfs because of I can not find the relationship between three point ; so we can think it just like this picture
each point 3 and 5 can have the 2_3and 2_5;3_5 child.and we can use a tricky in programming that if we meet 2_3=6 we should p2++;p3++;if 35:p5++;p3++;just like the code writ in DFS function*
public class Solution {
public int nthUglyNumber(int n) {
int[] dp=new int[n];dp[0]=1;
return DFS(dp,1,0,0,0,n);
}
private int DFS(int[] dp, int i, int p2, int p3, int p5, int n) {
if (i==n)return dp[n-1];
dp[i]=Math.min(dp[p2]*2, Math.min(dp[p3]*3,dp[p5]*5));
if (dp[i]==dp[p2]*2)p2++;
if(dp[i]==dp[p3]*3)p3++;
if (dp[i]==dp[p5]*5)p5++;
return DFS(dp, i+1, p2, p3, p5, n);
}
}
(Java) Version 4 Time: 2ms (By zmajia):
這個(gè)哈哈哈哈哈哈逗逼把測試樣例的極限試了出來,然后打表了哈哈哈哈哈哈铝侵,實(shí)在是太壞了灼伤,所以獲得了最快的速度,已知極限用打表的方式空間換時(shí)間還是很管用的
public class Solution {
static int[] save = new int[3000];
static int flag = 0;
public int nthUglyNumber(int n) {
if(flag == 0){
flag ++;
init();
}
return save[n-1];
}
public static void init(){
int _2 = 0, _3 = 0, _5 = 0;
save[0] = 1;
for(int i = 1; i < 3000; i++){
int min = Math.min(save[_2]*2, Math.min(save[_3]*3,save[_5]*5));
if(save[_2]*2 == min) {
save[i] = save[_2]*2;
_2 ++;
}
if(save[_3]*3 == min) {
save[i] = save[_3]*3;
_3 ++;
}
if(save[_5]*5 == min) {
save[i] = save[_5]*5;
_5 ++;
}
}
}
}
(Java) Version 5 Time: 108ms (By saharH):
TreeSet我沒有怎么研究過咪鲜,雖然慢但這是我看到的最簡潔的做法狐赡,其中起作用的應(yīng)該就是TreeSet的一些特性了,值得研究
public class Solution {
public int nthUglyNumber(int n) {
TreeSet<Long> hs = new TreeSet<>(Arrays.asList(1l, 2l, 3l, 5l));
Long e = 1l;
while( n != 0){
e = hs.pollFirst();
hs.add(e * 2);
hs.add(e * 3);
hs.add(e * 5);
n--;
}
return e.intValue();
}
}