• 丑数


    题目描述

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

    思路一

    依次判断每个数是不是丑数,直到判断到第N个丑数为止,时间复杂度过大,不推荐

    
        public boolean isUglyNumber(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;
        }
    
    
        public int GetUglyNumber_Solution_1(int index){
           int number = 0;
           int count = 0;
           while (count < index){
               number++;
               if(isUglyNumber(number)){
                   count++;
               }
    
           }
    
         return  number;
    

    思路二

    通俗易懂的解释:
    首先从丑数的定义我们知道,一个丑数的因子只有2,3,5,那么丑数p = 2 ^ x * 3 ^ y * 5 ^ z

    • 换句话说一个丑数一定由另一个丑数乘以2或者乘以3或者乘以5得到
    • 或者说一个丑数乘以2or3or5一定会是另外一个丑数

    那么我们从1开始乘以2,3,5,就得到2,3,5三个丑数,在从这三个丑数出发乘以2,3,5就得到(4,6,10),(6,9,15),(10,15,25)九个丑数,我们发现这种方法会得到重复的丑数,而且我们题目要求第N个丑数,这样的方法得到的丑数也是无序的。

    • 所以我们采用另外一种方法,设顺序排列的N个丑数的数组为result[N]

    • 首先从最小的丑数:reslut[0]=1开始,分别乘2,3,5得到三个数,然后选择最小的数2放入result[1] = 2

      那么reslut[2]一定是由前面的1和2分别乘上2,3,5某个数得到的,但是我们还不知道具体是哪个,所以需要将2乘上2,3,5,然后放入候选队列中,不过这里放入候选队列是有讲究的,所有乘以2的数在一个队列,所有乘以3的数在一个队列,所有乘以5的数在一个队列

    那么我们可以维护三个队列:
    (1)丑数数组: 1
    乘以2的队列:2
    乘以3的队列:3
    乘以5的队列:5
    选择三个队列头最小的数2加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;
    (2)丑数数组:1,2
    乘以2的队列:4
    乘以3的队列:3,6
    乘以5的队列:5,10
    选择三个队列头最小的数3加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;
    (3)丑数数组:1,2,3
    乘以2的队列:4,6
    乘以3的队列:6,9
    乘以5的队列:5,10,15
    选择三个队列头里最小的数4加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;
    (4)丑数数组:1,2,3,4
    乘以2的队列:6,8
    乘以3的队列:6,9,12
    乘以5的队列:5,10,15,20
    选择三个队列头里最小的数5加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;
    (5)丑数数组:1,2,3,4,5
    乘以2的队列:6,8,10,
    乘以3的队列:6,9,12,15
    乘以5的队列:10,15,20,25
    选择三个队列头里最小的数6加入丑数数组,但我们发现,有两个队列头都为6,所以我们弹出两个队列头,同时将12,18,30放入三个队列;

    image

    public class Solution {
       
        public int GetUglyNumber_Solution(int index) {
            if(index<=0)
                return 0;
            int[] result = new int[index];
            int count = 0;
            int i2 = 0;
            int i3 = 0;
            int i5 = 0;
    
            result[0] = 1;
            int tmp = 0;
            while (count < index-1) {
                //这里的result[i2] result[i3] result[i5] 分别代表三个队列的的第一个数,初始值都为1
                //他们分别乘以2,3,5,作为下一个的丑数候选者
                //事实上运行的时候每个队列里面只有一个数
                tmp = min(result[i2] * 2, min(result[i3] * 3, result[i5] * 5));
                //从候选者中选出最小的数作为下一个丑数,然后这个时候该队列的下标就向后移动一个,指向该队列的下一个候选者(而下一个候选者 = 当前队列的下标在result数组中指向的数 * 队列的类型数2or3or5)
                if(tmp==result[i2] * 2) i2++;//三条if防止值是一样的,不要改成else的
                if(tmp==result[i3] * 3) i3++;
                if(tmp==result[i5] * 5) i5++;
                result[++count]=tmp;
            }
            return result[index - 1];
        }
    
    
    
        private int min(int a, int b) {
            return (a > b) ? b : a;
        }
    }
    

    解析

    import java.util.Arrays;
    
    public class Test14 {
    
        public static void main(String[] args){
    
            Test14 test14 = new Test14();
            int result = test14.GetUglyNumber_Solution(5);
            System.out.println(result);
        }
    
    
        public int GetUglyNumber_Solution(int index) {
            if(index<=0)
                return 0;
            int[] result = new int[index];
            int count = 0;
            int i2 = 0;
            int i3 = 0;
            int i5 = 0;
    
            result[0] = 1;
            int tmp = 0;
            while (count < index-1) {
                int i3Value = result[i3] * 3;
                System.out.println("i3Value:" + i3Value);
                int i5Value = result[i5] * 5;
                System.out.println("i5Value:" + i5Value);
                int i2Value = result[i2] * 2;
                System.out.println("i2Value:" + i2Value);
                tmp = min(i2Value, min(i3Value, i5Value));
                System.out.println("最小值:" + tmp);
                if(tmp==result[i2] * 2) i2++;//三条if防止值是一样的,不要改成else的
                System.out.println("i2:" + i2);
                if(tmp==result[i3] * 3) i3++;
                System.out.println("i3:" + i3);
                if(tmp==result[i5] * 5) i5++;
                System.out.println("i5:" + i5);
                result[++count]=tmp;
                System.out.println("resul" + Arrays.toString(result));
            }
            return result[index - 1];
        }
    
    
    
        private int min(int a, int b) {
            return (a > b) ? b : a;
        }
    }
    
    
    
    i3Value:3
    i5Value:5
    i2Value:2
    最小值:2
    i2:1
    i3:0
    i5:0
    resul[1, 2, 0, 0, 0]
    i3Value:3
    i5Value:5
    i2Value:4
    最小值:3
    i2:1
    i3:1
    i5:0
    resul[1, 2, 3, 0, 0]
    i3Value:6
    i5Value:5
    i2Value:4
    最小值:4
    i2:2
    i3:1
    i5:0
    resul[1, 2, 3, 4, 0]
    i3Value:6
    i5Value:5
    i2Value:6
    最小值:5
    i2:2
    i3:1
    i5:1
    resul[1, 2, 3, 4, 5]
    5
    
    Process finished with exit code 0
    
    

    牛客网中科院大佬的官方解释

  • 相关阅读:
    jsADS的库(待更新)
    javascript设计模式工厂方法模式
    jQuery星级评价
    邮政编码联动地址
    ADS图像缩放与剪裁(只是完成了前台的功能,传送数据到后台的功能待完成)
    ie6png透明解决(ietester的ie6有问题,原生ie6是没问题的)
    javascript设计模式桥接模式
    每一个人都应该学会执着
    防止电脑被攻击
    获取用户的IP地址的三个属性的区别
  • 原文地址:https://www.cnblogs.com/flyingcr/p/10698549.html
Copyright © 2020-2023  润新知