1. 程式人生 > >牛客網-劍指offer-醜數

牛客網-劍指offer-醜數

題目

把只包含因子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為 1list.get(i2) 1list.get(i3) = 1list.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為 2list.get(i2) 2list.get(i3) = 1list.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為 3list.get(i2) 2list.get(i3) = 2list.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為 4list.get(i2) 3list.get(i3) = 2list.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為 5list.get(i2) 3list.get(i3) = 2list.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為 6list.get(i2) 4list.get(i3) = 3list.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為 7list.get(i2) 5list.get(i3) = 3list.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為 8list.get(i2) 5list.get(i3) = 4list.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為 9list.get(i2) 6list.get(i3) = 4list.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 的兩倍關係。