• 37.寻找丑数[Ugly numbers]


    【题目】

    我们把只包含质因子2、3和5的数称作丑数(Ugly Number),例如:2,3,4,5,6,8,9,10,12,15,等,习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第1500个丑数。

    【分析】

    这是一道在网络上广为流传的面试题,据说google曾经采用过这道题。

    所谓一个数m是另一个数n的因子,是指n能被m整除,也就是n % m == 0。根据丑数的定义,丑数只能被2、3和5整除。也就是说如果一个数如果它能被2整除,我们把它连续除以2;如果能被3整除,就连续除以3;如果能被5整除,就除以连续5。如果最后我们得到的是1,那么这个数就是丑数,否则不是。

    基于前面的分析,我们可以写出如下的函数来判断一个数是不是丑数:

    最普通(也最耗时)的做法是从1开始遍历,然后判断这个数的因式分解中只包含2,3,5,满足则找到了一个,一直找下去,直到第n个被找出!测试了一下,找第1500个丑数耗时40秒!

    【方案1】

     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
     

    bool IsUglyNumber(int n)
    {
        
    while(n % 2 == 0)
            n /= 
    2;
        
    while(n % 3 == 0)
            n /= 
    3;
        
    while(n % 5 == 0)
            n /= 
    5;
        
    return n == 1;
    }

    int GetUglyNumber_Solution1(int index)
    {
        
    if (index <= 0)
            
    return 0;

        
    int number = 0;
        
    int uglyFound = 0;
        
    while(uglyFound < index)
        {
            number++;
            
    if (IsUglyNumber(number))
            {
                uglyFound++;
            }
        }
        
    return number;
    }

    我们只需要在函数GetUglyNumber_Solution1中传入参数1500,就能得到第1500个丑数。该算法非常直观,代码也非常简洁,但最大的问题我们每个整数都需要计算。即使一个数字不是丑数,我们还是需要对它做求余数和除法操作。因此该算法的时间效率不是很高。

    接下来我们换一种思路来分析这个问题,试图只计算丑数,而不在非丑数的整数上花费时间。根据丑数的定义,丑数应该是另一个丑数乘以2、3或者5的结果(1除外)。因此我们可以创建一个数组,里面的数字是排好序的丑数。里面的每一个丑数是前面的丑数乘以2、3或者5得到的。

    假设数组ugly[N]中存放不断产生的丑数,初始只有一个丑数ugly[0]=1,由此出发,下一个丑数由因子2,3,5竞争产生,得到ugly[0]*2, ugly[0]*3, ugly[0]*5, 显然最小的那个数是新的丑数,所以第2个丑数为ugly[1]=2,开始新一轮的竞争,由于上一轮竞争中,因子2获胜,这时因子2对应的索引应该加1,变为1,得到ugly[1]*2,ugly[0]*3,ugly[0]*5, 因子3获胜,ugly[2]=3,同理,下次竞争时因子3对应的索引应该加1,变为1,即:ugly[1]*2, ugly[1]*3, ugly[0]*5, 因子5获胜,得到ugly[3]=5,重复这个过程,直到第n个丑数产生。总之:每次竞争中有一个(也可能是两个)因子胜出,下一次竞争中胜出的因子对应的索引应该加1!

    方案2】

     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
     

    int mymin(int a, int b, int c)
    {
        
    int tmp = a < b ? a : b;
        
    int res = tmp < c ? tmp : c;
        
    return res;
    }

    int GetUglyNumber_Solution2(int index)
    {
        
    if (index <= 0)
            
    return 0;
        
    int *ugly = new int[index];
        ugly[
    0] = 1;
        
    int index2 = 0;
        
    int index3 = 0;
        
    int index5 = 0;

        
    int uglyIndex = 1;
        
    while(uglyIndex < index)
        {
            
    int minValue = mymin(ugly[index2] * 2, ugly[index3] * 3, ugly[index5] * 5);
            
    // store ugly number
            ugly[uglyIndex] = minValue;
            uglyIndex++;

            
    // find index which gets minValue, and then move it next
            if (minValue == ugly[index2] * 2)
                index2++;
            
    if (minValue == ugly[index3] * 3)
                index3++;
            
    if (minValue == ugly[index5] * 5)
                index5++;
        }
        
    int result = ugly[index - 1];
        
    delete []ugly;
        
    return result;
    }

     【参考】

    http://zhedahht.blog.163.com/blog/static/2541117420094245366965/

    http://www.cppblog.com/zenliang/articles/131094.html

    http://www.cnblogs.com/coser/archive/2011/03/07/1976525.html

    个人学习笔记,欢迎拍砖!---by hellogiser

    Author: hellogiser
    Warning: 本文版权归作者和博客园共有,欢迎转载,但请保留此段声明,且在文章页面明显位置给出原文连接。Thanks!
    Me: 如果觉得本文对你有帮助的话,那么【推荐】给大家吧,希望今后能够为大家带来更好的技术文章!敬请【关注】
  • 相关阅读:
    Git本地操作2
    Blast在windows下的使用过程
    和为T
    出现次数最多的整数
    蓝桥杯 未名湖边的烦恼 java
    蓝桥杯数字三角形 java
    ①①将线性拉伸
    ⑩把线型对象转平面对象
    ⑨矩形
    ⑧建立样条:(样条也能够被拉伸)
  • 原文地址:https://www.cnblogs.com/hellogiser/p/find-the-1500th-ugly-number.html
Copyright © 2020-2023  润新知