《劍指office》醜數


題目描述
把只包含因子2、3和5的數稱作醜數(Ugly Number)。例如6、8都是醜數,但14不是,因爲它包含因子7。 習慣上我們把1當做是第一個醜數。求按從小到大的順序的第N個醜數。

這一題我想了很久,自己想的方法時間複雜度都太高,所有都超時了,導致一上午都在糾結這個問題,最後看別人的答案才知道大神都是怎麼解決這個問題的,仔細想想,還是自己的算法基礎不太好。好了,廢話不多說,直接上思路。
遍歷法:時間效率低下
  使用遍歷法求第k個醜數,從1開始遍歷,如果是醜數則count++,直到count=k爲止。那麼如何判斷醜數呢?根據醜數的定義,醜數只有2,3,5這三個因子,那麼我們就拿數字除以這三個因子。具體算法如下:
Step1.如果一個數能夠被2整除,那麼讓他繼續除以2;
Step2.如果一個數能夠被3整除,那麼讓他繼續除以3;
Step3.如果一個數能夠被5整除,那麼讓他繼續除以5;
Step4.如果最後這個數變爲1,那麼這個數就是醜數,否則不是。
public int GetUglyNumber(int index)
    {
        if (index <= 0)
        {
            return 0;
        }


        int number = 0;
        int uglyCount = 0;


        while (uglyCount < index)
        {
            number++;


            if (IsUgly(number))
            {
                uglyCount++;
            }
        }


        return number;
    }


    private bool IsUgly(int number)
    {
        while (number % 2 == 0)
        {
            number /= 2;
        }


        while (number % 3 == 0)
        {
            number /= 3;
        }


        while (number % 5 == 0)
        {
            number /= 5;
        }


        return number == 1 ? true : false;
    }

該算法非常直觀,代碼也非常簡潔,但最大的問題就在於每個整數都需要計算。即使一個數字不是醜數,我們還是需要對它做求餘數和除法操作。因此該算法的時間效率不是很高,
空間換時間法:時間效率較高
  根據醜數的定義,我們可以知道醜數可以由另外一個醜數乘以2,3或者5得到。因此我們可以創建一個數組,裏面的數字是排好序的醜數,每一個醜數都是前面的醜數乘以2,3或者5得到的。
  我們把得到的第一個醜數乘以2以後得到的大於M的結果記爲M2。同樣,我們把已有的每一個醜數乘以3和5,能得到第一個大於M的結果M3和M5。那麼M後面的那一個醜數應該是M2,M3和M5當中的最小值:Min(M2,M3,M5)比如將醜數數組中的數字按從小到大乘以2,直到得到第一個大於M的數爲止,那麼應該是2*2=4<M,3*2=6>M,所以M2=6。同理,M3=6,M5=10。所以下一個醜數應該是6。
  根據以上思路實現代碼如下:

public int GetUglyNumber(int index)
    {
        if (index <= 0)
        {
            return 0;
        }


        int[] uglyNumbers = new int[index];
        uglyNumbers[0] = 1;
        int nextUglyIndex = 1;


        int multiply2 = 0;
        int multiply3 = 0;
        int multiply5 = 0;
        int min = 0;


        while (nextUglyIndex < index)
        {
            min = Min(uglyNumbers[multiply2] * 2, uglyNumbers[multiply3] * 3, uglyNumbers[multiply5] * 5);
            uglyNumbers[nextUglyIndex] = min;


            while (uglyNumbers[multiply2] * 2 <= uglyNumbers[nextUglyIndex])
            {
                multiply2++;
            }


            while (uglyNumbers[multiply3] * 3 <= uglyNumbers[nextUglyIndex])
            {
                multiply3++;
            }


            while (uglyNumbers[multiply5] * 5 <= uglyNumbers[nextUglyIndex])
            {
                multiply5++;
            }


            nextUglyIndex++;
        }


        int result = uglyNumbers[index - 1];
        uglyNumbers = null;


        return result;
    }


    private int Min(int num1, int num2, int num3)
    {
        int min = num1 < num2 ? num1 : num2;
        min = min < num3 ? min : num3;


        return min;
    }

和第一種方案相比,第二種方案不需要在非醜數的整數上做任何計算,因此時間效率有明顯提升。但也需要指出,第二種算法由於需要保存已經生成的醜數,因此需要一個數組,從而增加了空間消耗。如果是求第1500個醜數,將創建一個能容納1500個醜數的數組,這個數組佔內存6KB。
發佈了37 篇原創文章 · 獲贊 80 · 訪問量 4010
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章