題目描述
把只包含因子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。