丑数

题目描述

把只包含质因子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

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

原文地址:https://www.cnblogs.com/flyingcr/p/10698549.html