[剑指offer]面试题34:丑数
面試題34:丑數
題目:我們把只包含因子2、3和5的數稱作丑數(Ugly Number)。求按從小到大的順序的第1500個丑數。例如6、8都是丑數,但14不是,因為它包含因子7。習慣上我們把1當做第一個丑數。
? 逐個判斷每個整數是不是丑數的解法,直觀但不夠高效
代碼如下:
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; }int GetUglyNumber(int index) {if (index <= 0) return 0;int number = 0;int uglyFound = 0;while (uglyFound < index){++number;if (IsUgly(number)){++uglyFound;}}return number; }? 創建數組保存已經找到的丑數,用空間換時間的解法
前面的算法之所以效率低,很大程度上是因為不管一個數是不是丑數我們對它都要作計算。
接下來我們試著找到一種只要計算丑數的方法,而不在非丑數的整數上花費時間。
根據丑數的定義,丑數應該是另一個丑數乘以2、3或者5的結果(1除外)。因此我們可以創建一個數組,里面的數字是排好序的丑數,每一個丑數都是前面的丑數乘以2、3或者5得到的。
這種思路的關鍵在于怎樣確保數組里面的丑數是排好序的。
假設數組中已經有若干個丑數排好序后存放在數組中,并且把已有最大的丑數記做M,我們接下來分析如何生成下一個丑數。
該丑數肯定是前面某一個丑數乘以2、3或者5的結果,所以我們首先考慮把已有的每個丑數乘以2。
在乘以2的時候,能得到若干個小于或等于M的結果。
由于是按照順序生成的,小于或者等于M肯定已經在數組中了,我們不需再次考慮;還會得到若干個大于M的結果,但我們只需要第一個大于M的結果,因為我們希望丑數是按從小到大的順序生成的,其他更大的結果以后再說。
我們把得到的第一個乘以2后大于M的結果記為M2。同樣,我們把已有的每一個丑數乘以3和5,能得到第一個大于M的結果M3和M5。那么下一個丑數應該是M2、M3和M5這3個數的最小者。
前面分析的時候,提到把已有的每個丑數分別都乘以2、3和5。
事實上這不是必須的,因為已有的丑數是按順序存放在數組中的。
對乘以 2 而言,肯定存在某一個丑數T2,排在它之前的每一個丑數乘以2得到的結果都會小于已有最大的丑數,在它之后的每一個丑數乘以 2 得到的結果都會太大。
我們只需記下這個丑數的位置,同時每次生成新的丑數的時候,去更新這個T2。對乘以3和5而言,也存在著同樣的T3和T5。
有了這些分析,我們就可以寫出如下代碼:
int GetUglyNumber_Solution2(int index) {if (index <= 0) return 0;int *pUglyNumbers = new int[index];pUglyNumbers[0] = 1;int nextUglyIndex = 1;int *pMultiply2 = pUglyNumbers;int *pMultiply3 = pUglyNumbers;int *pMultiply5 = pUglyNumbers;while (nextUglyIndex < index){int min = Min(*pMultiply2 * 2, *pMultiply3 * 3, *pMultiply5 * 5);pUglyNumbers[nextUglyIndex] = min;while (*pMultiply2 * 2 <= pUglyNumbers[nextUglyIndex]) ++pMultiply2;while (*pMultiply3 * 3 <= pUglyNumbers[nextUglyIndex]) ++pMultiply3;while (*pMultiply5 * 5 <= pUglyNumbers[nextUglyIndex]) ++pMultiply5;++nextUglyIndex;}int ugly = pUglyNumbers[nextUglyIndex - 1];delete[] pUglyNumbers;return ugly; }int Min(int number1, int number2, int number3) {int min = (number1 < number2) ? number1 : number2;min = (min < number3) ? min : number3;return min; }和第一種思路相比,第二種思路不需要在非丑數的整數上做任何計算,因此時間效率有明顯提升。但也需要指出,第二種算法由于需要保存已經生成的丑數,因此需要一個數組,從而增加了空間消耗。如果是求第 1500個丑數,將創建一個能容納1500個丑數的數組,這個數組占內存6KB。而第一種思路沒有這樣的內存開銷??偟膩碚f,第二種思路相當于用較小的空間消耗換取了時間效率的提升。
測試用例:
● 功能測試(輸入2、3、4、5、6等)。
● 特殊輸入測試(邊界值1、無效輸入0)。
● 性能測試(輸入較大的數字,如1500)。
本題考點:
● 考查應聘者對時間復雜度的理解。絕大部分應聘者都能想出第一種思路。在面試官提示還有更快的解法之后,應聘者能否分析出時間效率的瓶頸,并找出解決方案,是能否通過這輪面試的關鍵。
● 考查應聘者的學習能力和溝通能力。丑數對很多人而言是個新概念。有些面試官喜歡在面試的時候定義一個新概念,然后針對這個新概念出面試題。這就要求應聘者聽到不熟悉的概念之后,要有主動積極的態度,大膽向面試官提問,經過幾次思考、提問、再思考的循環,在短時間內理解這個新概念。這個過程就體現了應聘者的學習能力和溝通能力。
總結
以上是生活随笔為你收集整理的[剑指offer]面试题34:丑数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 倪光南院士:发展开源RISC-V基础软件
- 下一篇: 小米第三季度净利润 59.9 亿元同比增