牛客網-劍指offer-醜數
阿新 • • 發佈:2019-02-20
題目
把只包含因子2、3和5的數稱作醜數(Ugly Number)。
例如6、8都是醜數,但14不是,因為它包含因子7。
習慣上我們把1當做是第一個醜數。
求按從小到大的順序的第N個醜數。
思路:
1. 暴力法:
先判斷一個數是不是 醜數,然後從0開始判斷,知道符合 醜數的數目 累加到 N 。
2. 三個取最小值法: 一個醜數必然是 2 ,3,5 這個三個因為的 一個 或多個相乘。 初始的幾個數為 [ 1,2,3,5 ] 對嗎? 不對。 初始的幾個資料為 **[ 1,2,3, 4, 5 ]** 4 為二乘了兩次。然後呢,是 3*2 =6 。這個 6 又是怎麼比較出來的呢。 我用 3*2 ==》 所有2 的倍數,並且因子也是醜數的,因為, 1*2 ,2*2 ,已經用過了,現在只能用 3*2 了。 2*3 ==》 同理,所有 3的倍數,並且因子也是醜數的, 1*3 已經用過了。 2*5 ==》 所有 5的倍數,並且因子也是醜數的, 1*5 已經用過了。 所以上述的方法每次只需要比較三個數。並且不遺不漏。
程式碼如下:
public class Solution {
public int GetUglyNumber_Solution(int index) {
if(index<=0)return 0;
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(1); // 1
int i2=0;
int i3=0;
int i5=0;
int min=0;
for(int i=1;i<index;i++){
int m2 = list.get(i2)*2;
int m3 = list.get(i3)*3;
int m5 = list.get(i5)*5;
System.out.println("i為 "+i+" , m2 = "+ m2 +" , m3 = "+ m3 +" , m5 = "+ m5 );
System.out.println("i2 為 " +i2 +",i3 為 " +i3 + ",i5 為 " +i5 );
System.out .println("i為 "+i+" , list.get(i2) "+ list.get(i2) +" , list.get(i3) = "+ list.get(i3) +" , list.get(i5) = "+ list.get(i5) );
min = Math.min(m2,Math.min(m3,m5));
if(min == m2){
i2++;
}
if(min == m3){
i3++;
}
if(min == m5){
i5++;
}
System.out.println("i為 "+i+" , m2 = "+ m2 +" , m3 = "+ m3 +" , m5 = "+ m5 +" , min = "+min);
System.out.println("i2 為 " +i2 +",i3 為 " +i3 + ",i5 為 " +i5 );
list.add(min);
System.out.println(list);
System.out.println( );
System.out.println( );
}
return list.get(list.size()-1);
}
}
運算的結果如下:
i為 1 , m2 = 2 , m3 = 3 , m5 = 5
i2 為 0,i3 為 0,i5 為 0
i為 1 , list.get(i2) 1 , list.get(i3) = 1 , list.get(i5) = 1
i為 1 , m2 = 2 , m3 = 3 , m5 = 5 , min = 2
i2 為 1,i3 為 0,i5 為 0
[1, 2]
i為 2 , m2 = 4 , m3 = 3 , m5 = 5
i2 為 1,i3 為 0,i5 為 0
i為 2 , list.get(i2) 2 , list.get(i3) = 1 , list.get(i5) = 1
i為 2 , m2 = 4 , m3 = 3 , m5 = 5 , min = 3
i2 為 1,i3 為 1,i5 為 0
[1, 2, 3]
i為 3 , m2 = 4 , m3 = 6 , m5 = 5
i2 為 1,i3 為 1,i5 為 0
i為 3 , list.get(i2) 2 , list.get(i3) = 2 , list.get(i5) = 1
i為 3 , m2 = 4 , m3 = 6 , m5 = 5 , min = 4
i2 為 2,i3 為 1,i5 為 0
[1, 2, 3, 4]
i為 4 , m2 = 6 , m3 = 6 , m5 = 5
i2 為 2,i3 為 1,i5 為 0
i為 4 , list.get(i2) 3 , list.get(i3) = 2 , list.get(i5) = 1
i為 4 , m2 = 6 , m3 = 6 , m5 = 5 , min = 5
i2 為 2,i3 為 1,i5 為 1
[1, 2, 3, 4, 5]
i為 5 , m2 = 6 , m3 = 6 , m5 = 10
i2 為 2,i3 為 1,i5 為 1
i為 5 , list.get(i2) 3 , list.get(i3) = 2 , list.get(i5) = 2
i為 5 , m2 = 6 , m3 = 6 , m5 = 10 , min = 6
i2 為 3,i3 為 2,i5 為 1
[1, 2, 3, 4, 5, 6]
i為 6 , m2 = 8 , m3 = 9 , m5 = 10
i2 為 3,i3 為 2,i5 為 1
i為 6 , list.get(i2) 4 , list.get(i3) = 3 , list.get(i5) = 2
i為 6 , m2 = 8 , m3 = 9 , m5 = 10 , min = 8
i2 為 4,i3 為 2,i5 為 1
[1, 2, 3, 4, 5, 6, 8]
i為 7 , m2 = 10 , m3 = 9 , m5 = 10
i2 為 4,i3 為 2,i5 為 1
i為 7 , list.get(i2) 5 , list.get(i3) = 3 , list.get(i5) = 2
i為 7 , m2 = 10 , m3 = 9 , m5 = 10 , min = 9
i2 為 4,i3 為 3,i5 為 1
[1, 2, 3, 4, 5, 6, 8, 9]
i為 8 , m2 = 10 , m3 = 12 , m5 = 10
i2 為 4,i3 為 3,i5 為 1
i為 8 , list.get(i2) 5 , list.get(i3) = 4 , list.get(i5) = 2
i為 8 , m2 = 10 , m3 = 12 , m5 = 10 , min = 10
i2 為 5,i3 為 3,i5 為 2
[1, 2, 3, 4, 5, 6, 8, 9, 10]
i為 9 , m2 = 12 , m3 = 12 , m5 = 15
i2 為 5,i3 為 3,i5 為 2
i為 9 , list.get(i2) 6 , list.get(i3) = 4 , list.get(i5) = 3
i為 9 , m2 = 12 , m3 = 12 , m5 = 15 , min = 12
i2 為 6,i3 為 4,i5 為 2
[1, 2, 3, 4, 5, 6, 8, 9, 10, 12]
12
總結:
* 這種思路的關鍵在於怎樣確保數組裡面的醜數是排好序的。
* 我們假設陣列中已經有若干個醜數,排好序後存在陣列中。
* 我們把現有的最大丑數記做M。
* 現在我們來生成下一個醜數,該醜數肯定是前面某一個醜數乘以2、3或者5的結果。
* 我們首先考慮把已有的每個醜數乘以2。在乘以2的時候,能得到若干個結果小於或等於M的。
* 由於我們是按照順序生成的,小於或者等於M肯定已經在陣列中了,我們不需再次考慮;
* 我們還會得到若干個大於M的結果,但我們只需要第一個大於M的結果,因為我們希望醜數是按從小到大順序生成的,其他更大的結果我們以後再說。
* 我們把得到的第一個乘以2後大於M的結果,記為M2。
* 同樣我們把已有的每一個醜數乘以3和5,能得到第一個大於M的結果M3和M5。
* 那麼下一個醜數應該是M2、M3和M5三個數的最小者。
*
*
*前面我們分析的時候,提到把已有的每個醜數分別都乘以2、3和5,事實上是不需要的,
*因為已有的醜數是按順序存在陣列中的。對乘以2而言,肯定存在某一個醜數T2,
*排在它之前的每一個醜數乘以2得到的結果都會小於已有最大的醜數,在它之後的每一個醜數乘以2得到的結果都會太大。
*我們只需要記下這個醜數的位置,同時每次生成新的醜數的時候,去更新這個T2。
* 對乘以3和5而言,存在著同樣的T3和T5。
*
* 大智小結: 1. 任何一個醜數 都是 2、3、5的倍數
* 2. 獲得 一個醜數需要比較 三個數字 m2 ,m3,m5 。而m2 ,一定是上一個 m2 的兩倍關係。