面试题 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;
}

若考虑大树问题,需要重载乘法,也就是需要重载加法。

posted on 2014-01-27 05:42  Felix Fang  阅读(425)  评论(0)    收藏  举报

导航