剑指offer(49-丑数) 题解

剑指offer-49-丑数

微信搜索【程序员画工师】关注更多Java编程技术、数据结构与算法、面试题相关内容。

题目

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

思路1-遍历法

使用遍历法求第k个丑数,从1开始遍历,如果是丑数则count++,直到count=k为止。那么如何判断丑数呢?根据丑数的定义,丑数只有2,3,5这三个因子,那么我们就拿数字除以这三个因子。具体算法如下:

•Step1.如果一个数能够被2整除,那么让他继续除以2;
•Step2.如果一个数能够被3整除,那么让他继续除以3;
•Step3.如果一个数能够被5整除,那么让他继续除以5;
•Step4.如果最后这个数变为1,那么这个数就是丑数,否则不是。

上代码


public class Solution {
    public int GetUglyNumber_Solution(int index) {
        if(index <= 0){
            return 0;
        }

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

    public boolean 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;
    }
}

思路2-空间换时间

根据丑数的定义,我们可以知道丑数可以由另外一个丑数乘以2,3或者5得到。因此我们可以创建一个数组,里面的数字是排好序的丑数,每一个丑数都是前面的丑数乘以2,3或者5得到的。
事实上我们不需要每次都计算前面所有丑数乘以2,3,5的结果,然后再比较大小。因为在已存在的丑数中,一定存在某个数T2,在它之前的所有数乘以2都小于已有丑数,而T2×2的结果一定大于最大的丑数,同理,也存在这样的数T3,T5,我们只需要标记这三个数即可。
首先,丑数都是由前5个丑数1,2,3,4,5乘以因子2,3,5才得到的。这里将得到的丑数都存放在数组中。分析一下得到第6个丑数的过程,其他丑数类比。
将前5个丑数乘以2,然后与第5个丑数进行比较,找出第一个乘以2大于第5个丑数的丑数,将其放在min2中。
将前5个丑数乘以3,然后与第5个丑数进行比较,找出第一个乘以3大于第5个丑数的丑数,将其放在min3中。
将前5个丑数乘以5,然后与第5个丑数进行比较,找出第一个乘以5大于第5个丑数的丑数,将其放在min5中。
经过上述,第6个丑数必然是min2,min3,min5中最小的那个丑数。比较这三个数,就可以得到第6个丑数。

上代码


public class Solution {
    public int GetUglyNumber_Solution(int index) {
        if(index <= 0){
            return 0;
        }
        if(index == 1){
            return 1;
        }

        int[] uglyNumbers = new int[index];
        uglyNumbers[0] = 1;
        int nextUglyIndex = 1;
        int ugly2 = 0;
        int ugly3 = 0;
        int ugly5 = 0;

        while(nextUglyIndex < index){
            int min = Math.min(uglyNumbers[ugly2] * 2, Math.min(uglyNumbers[ugly3] * 3, uglyNumbers[ugly5] * 5));
            uglyNumbers[nextUglyIndex] = min;

            while(uglyNumbers[ugly2] * 2 <= uglyNumbers[nextUglyIndex]){
                ugly2 ++;
            }
            while(uglyNumbers[ugly3] * 3 <= uglyNumbers[nextUglyIndex]){
                ugly3 ++;
            }
            while(uglyNumbers[ugly5] * 5 <= uglyNumbers[nextUglyIndex]){
                ugly5 ++;
            }
            nextUglyIndex ++;
        }

        int uglyNum = uglyNumbers[nextUglyIndex-1];

        return uglyNum;
    }
}

思考

和第一种方案相比,第二种方案不需要在非丑数的整数上做任何计算,因此时间效率有明显提升。但也需要指出,第二种算法由于需要保存已经生成的丑数,因此需要一个数组,从而增加了空间消耗。如果是求第1500个丑数,将创建一个能容纳1500个丑数的数组,这个数组占内存6KB。

References

[1] 《剑指offer(第二版)》 何海涛著

程序员画工师公众号,获取更多详细Java、Python、C、前端、小程序、产品相关学习资料,欢迎交流
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值