面试题 34(*),丑数(Ugly Number) (用的也是递推思想)

思路:由于丑数必然是等于一个较小的丑数与2,3或5相乘的结果。
因此,假设我们已经拥有了从小到大排好队的丑数序列,当需要寻找新的丑数时,它必然是来自这个序列中的某一个数乘以2,3,5。我们可以将队列中每一个数字都乘上2,3,5,然后找一个大于当前队列最大值的最小丑数,放入队列。
这样就可以用递归或者循环做了。
这个思路可以进一步优化:我们真的需要每次都把队列中所有的数字都乘以2, 3, 5吗?
假设我们找到的新丑数,是位于T1位置老丑数乘以2所得的结果,那么接着再找下一次丑数时,我们肯定不再需要考虑T1位置及T1位置往前的数字乘以2的结果,因为他们必然比当前队列的最后一个丑数小。所以,乘以2方面,T1位置下一个的数,最有竞争力的竞选者,这个数字再往后乘以2就偏大了。
同理,如果最近的丑数是位于T2位置的数乘以3得到的,在乘以3所得的候选者上,我们只要考虑T2下一个数就可以了。
因此,每次比较,我们不需要把所有的数都乘以 2 3 5,然后慢慢选。我们只需要从三个数中挑选就可以了。
当然,需要我们需要问考官要不要考虑大数问题,有的丑数已经超出了整型范围。
如果不考虑大树问题,代码会相对简单,
用循环实现就可以,代码:
#include <stdio.h> int getNearestCandi(int *list, int &place, int curSize, int factor){ while(place <= curSize && list[place] * factor <= list[curSize]) place++; return list[place] * factor; } int findUglyNum(int *list, int size){ int T1 = 0, T2 = 0, T3 = 0; for(int i = 1;i < size;i++){ int candiT1 = getNearestCandi(list, T1, i-1, 2); int candiT2 = getNearestCandi(list, T2, i-1, 3); int candiT3 = getNearestCandi(list, T3, i-1, 5); int newUglyNum = 0; if(candiT1 <= candiT2 && candiT1 <= candiT3){ T1 = T1 + 1; newUglyNum = candiT1; }else if(candiT2 <= candiT1 && candiT2 <= candiT3){ T2 = T2 + 1; newUglyNum = candiT2; }else if(candiT3 <= candiT1 && candiT3 <= candiT2){ T3 = T3 + 1; newUglyNum = candiT3; } //printf("%dth number: %d\n", i+1, newUglyNum); list[i] = newUglyNum; } } int FindNthUglyNum(int nth){ if(nth <= 0) return 0; int * list = new int[nth]; list[0] = 1; for(int i = 1;i < nth;i++){ list[i] = 0; } findUglyNum(list, nth); return list[nth-1]; } void Test(int index, int expected) { if(FindNthUglyNum(index) == expected) printf("solution2 passed\n"); else printf("solution2 failed\n"); } int main() { Test(1, 1); Test(2, 2); Test(3, 3); Test(4, 4); Test(5, 5); Test(6, 6); Test(7, 8); Test(8, 9); Test(9, 10); Test(10, 12); Test(11, 15); Test(1500, 859963392); Test(0, 0); return 0; }
测试已经通过。
书上代码也没有考虑大数问题,算法2为上述递推思想,代码:
// UglyNumber.cpp : Defines the entry point for the console application. // // 《剑指Offer——名企面试官精讲典型编程题》代码 // 著作权所有者:何海涛 #include "stdafx.h" // ====================算法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_Solution1(int index) { if(index <= 0) return 0; int number = 0; int uglyFound = 0; while(uglyFound < index) { ++number; if(IsUgly(number)) { ++uglyFound; } } return number; } // ====================算法2的代码==================== int Min(int number1, int number2, int number3); 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; } // ====================测试代码==================== void Test(int index, int expected) { if(GetUglyNumber_Solution1(index) == expected) printf("solution1 passed\n"); else printf("solution1 failed\n"); if(GetUglyNumber_Solution2(index) == expected) printf("solution2 passed\n"); else printf("solution2 failed\n"); } int _tmain(int argc, _TCHAR* argv[]) { Test(1, 1); Test(2, 2); Test(3, 3); Test(4, 4); Test(5, 5); Test(6, 6); Test(7, 8); Test(8, 9); Test(9, 10); Test(10, 12); Test(11, 15); Test(1500, 859963392); Test(0, 0); return 0; }
若考虑大树问题,需要重载乘法,也就是需要重载加法。
------------------------------------------------
Felix原创,转载请注明出处,感谢博客园!
posted on 2014-01-27 05:42 Felix Fang 阅读(425) 评论(0) 收藏 举报
浙公网安备 33010602011771号