寻找丑数

算法题:寻找丑数

这是一道在订阅的blog上看到的题目,觉得比较有意思,就动手做了一下。

何为丑数?

丑数(Ugly number)是指其因子中只包含2、3、5的整数,类如6、20、36都是丑数,而7、19、28则不是丑数。特别的:1是最小的丑数

题目:求从小大到的情况下第1500个丑数

解题过程:

  1. 原始解法:

从1开始,判断当前数字是否丑数,不是则跳至下一个数字,是的话下标+1,如果下标增加到指定的数字,则此时对应的数字为要求的丑数。这个解法简单粗暴,可以说毫无技术含量。因为每一个数都要与2、3、5辗转相处,并且要遍历答案之前的每一个自然数,时间复杂度太差。

  1. 改进解法:

实际上我们可以试图跳过那些不是丑数的自然数,考虑一下丑数是怎么得来的:从1开始,12=2,13=3,1*5=5,然后从得到的2、3、5继续乘以对应的2、3、5得到新的数字。这样得到的数字都是丑数,因为不可能包含其他的因子。那么我们可以尝试着来构造一个从小到大排列的数组,数组的长度就是我们需要的丑数序号。
接下来的问题是:如何保持数组在构造的过程中是有序的?
假设我们现在有了部分数组,要添加一个新的丑数。新的丑数必然是从已有的数中,分别乘2、3、5之后获得,为保持有序我们只取其中最小的数字,因为这三个数字中间也许还有其他的丑数。实际上这样存在不必要的计算:可能存在一些数字,乘以对应的2、3、5之后也比当前存在的最大丑数要小,而我们只需要乘以2、3、5之后比当前最大丑数大的那个数。所以,对应2、3、5这三个因子,我们应该保存三个位置,使得这三个位置上的数字乘以对应的2、3、5之后比当前最大丑数大。怎么找?很简单,每次插入新的丑数之后,我们更新一下这三个数的位置就可以了。下一次就对这三个位置的数字分别乘以2、3、5,找出最小的插入数组。这样我们就保证了数组有序,并尽量减少了不必要的计算。

C++代码实现:

#include 

int get_min_number(int a, int b, int c)
{
    int min_ab = std::min(a,b);
    return std::min(min_ab, c);
}
int find_ugly_number(int target)
{
    if (target < 1)
        return 0;
    int* array = new int[target];
    array[0] = 1;
    int *pos2 = array;
    int *pos3 = array;
    int *pos5 = array;
    int index = 1;

    while (index < target)
    {
        int cur_max = get_min_number(*pos2*2, *pos3*3,*pos5*5);
        array[index++] = cur_max;

        while (*pos2*2 <= cur_max)
            pos2++;
        while (*pos3*3 <= cur_max)
            pos3++;
        while (*pos5*5 <= cur_max)
            pos5++;
    }

    int ret = array[target-1];
    delete [] array;
    return ret;
}


int main()
{
    int index = 1500;
    int result = find_ugly_number(index);
    std::cout << result << std::endl;
    return 0;
}

代码也被我放在我Github的issues库中,点击查看

你可能感兴趣的:(寻找丑数)