剑指Offer-49:丑数

题目描述

把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。

什么叫质因子

以255这个数为例讲解
质因子就是质数的因子,也称质因数或质约数。 255的因子有1 、3、5、15、17、51、85、255。其中是质数的是1、3、5、17 所以255的质因子就是1、3、5、17。所以最大质因子是17。55不是255的因子,也不是质数,当然不是255的最大质因子。


解题思路

判断一个数是不是丑数,最简单的方法就是让这个数不断除以2,3,5。要求第N个丑数,只要从1开始,依次判断每个数是不是丑数,如果是,则相应的序号加1,直到序号为N,就是我们要的丑数了。

方法一:
根据丑数的定义,丑数只能被2、3、5整除,因此,如果一个数能够被2整除,就连续除以2,能被3整除就连续整除3,能被5整除就连续除以5 。如果最后得到的结果为1,那么这个数就是整数,否则不是。因此通过一个函数用于判断该数是否为丑数,然后遍历找到对应位置的数字就好了。

class Solution1{
   /// 用于遍历数字是否为丑数,从而找出指定位置的丑数
   public int GetUglyNumber(int index){
       if (index <= 0)
           return 0;

       int number = 0;                         //递增的数字,用于判断是否为丑数
       int uglyNumber = 0;                     //丑数的数量

       //如果丑数的数量到达所要求的第index位了,则输出它
       while (uglyNumber < index) {
           number++;
           if (IsUgly(number))
               uglyNumber++;
       }

       return number;
   }

   /// 用于判断一个数是否为丑数,即能够整除2、3、5之后,最后得到的因子为1,则为丑数
   public bool IsUgly(int number){
       //能被2整除,则连续除以2
       while (number % 2 == 0)
           number /= 2;
       //能被3整除,则连续除以3
       while (number % 3 == 0)
           number /= 3;
       //能被5整除,则连续除以5
       while (number % 5 == 0)
           number /= 5;
       //除完之后的结果如果为1,说明其不包含其他因子,返回真,否则返回假
       return number == 1 ? true : false;
   }
}

优缺点: 方法十分直观,输入1500之后就能够得到第1500个丑数,代码也十分简洁。但是最大的问题就是计算量太大,时间效率不够高,对于每一个数不管是否为丑数我们都会去进行求余和除法操作,因此需要寻找新的更优算法才行。
这种方法时间效率很,通常面试官不会满意这样的答案。因此我们需要一个时间复杂度更低的解法。


方法二:额外定义个缓存数组,将丑数存下来
换个思路,我们只求丑数,不要去管非丑数。每个丑数必然是由小于它的某个丑数乘以2,3或5得到的,这样我们把求得的丑数都保存下来,用之前的丑数分别乘以2,3,5,找出这三这种最小的并且大于当前最大丑数的值,即为下一个我们要求的丑数。这种方法用空间换时间,时间复杂度为O(n)。

注意里面使用3个变量跟踪的技巧:
t2表示该位置之前的数都乘过2了
t3表示该位置之前的数都乘过3了
t5表示该位置之前的数都乘过5了

public class Solution {
    public int GetUglyNumber_Solution(int index) {
        if(index <= 0)
            return 0;
        if(index == 1)
            return 1;
        int t2 = 0, t3 = 0, t5 = 0;
        int [] res = new int[index];
        res[0] = 1;
        for(int i = 1; i

与第一种思路相比,第二种思路不需要在非丑数的整数上进行任何计算,因此时间效率会有显著的提升。但是,第二种算法由于需要保存已经生成的丑数,因此需要一个数组,从而增加了空间消耗。如果是求第1500个丑数,则将创建一个能够容纳1500个整数的数组,这个数组占据6KB的内容空间。而第一种思路则没有这样的内存开销。总体上来说,第二种思路相当于用较小的空间消耗换取了时间效率的提升,还是比较好的了。

在这里有点提及就是: 硬件的发展一直遵循着摩尔定律,内存的容量基本上每个18个月就会翻一番,由于内存的容量增加迅速,在软件开发的过程中,更多的时候对于牺牲一定空间优化时间性能的做法是允许的,以尽可能地缩短软件的响应时间。也就是算法中长提及的“空间换时间”。

你可能感兴趣的:(算法)