• 丑数


    题目描述

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


    思路

    丑数:只包含质因子2、3、5的数称作丑数。(说通俗点:一个数的所有因子中,为质数的因子只能是2、3、5)

    关键点(难点):丑数p = 2 ^ x * 3 ^ y * 5 ^ z;

    即每个丑数都满足上述式子,只是x、y、z取值不同生成了不同的丑数,

    同时得出一个丑数p1一定是由另一个小于它的丑数p2乘以2 or 乘以3 or 乘以5得到的。

    如:

    1. 1是最小(也是最初的丑数),于是我们分别将1与2、3、5相乘→得到2、3、5三个丑数;
    2. 然后由2、3、5出发我们又会得到:4,6,10,6,9,15,10,15,25九个丑数;
    3. ……

    但是这样得到的丑数是有重复且无序的,题目要求我们求按从小到大的顺序的第N个丑数,即要求我们得出的丑数是有序排列的(升序)。

    那么:

    • 先定义一个队列a用来存储丑数,a[0]=1;

    • 当由1得出2、3、5三个丑数时,我们可以取新生成的丑数中最小的丑数:2,然后2加入队列a,即a={1,2};

    • 紧接着我们由a[1]=2与2、3、5相乘得出新丑数:4、6、10,注意:此时4并不是当前的最小丑数,不要忘了第二部生成的3和5,因此,我们可以维护3个数列;

    • 假设有3个数列,分别为: 乘以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放入三个队列;

      ……………………

      疑问:

      1.为什么分三个队列?

      丑数数组里的数一定是有序的,因为我们是从丑数数组里的数乘以2,3,5选出的最小数,一定比以前未乘以2,3,5大,同时对于三个队列内部,按先后顺序乘以2,3,5分别放入,所以同一个队列内部也是有序的;

      2.为什么比较三个队列头部最小的数放入丑数数组?

      因为三个队列是有序的,所以取出三个头中最小的, 大专栏  丑数等同于找到了三个队列所有数中最小的。

      实现思路:

      我们没有必要维护三个队列只需要记录三个指针显示到达哪一步;“|”表示指针,arr表示丑数数组;

      (1)1

      |2

      |3

      |5

      目前指针指向0,0,0,队列头arr[0] * 2 = 2, arr[0] 3 = 3, arr[0] 5 = 5

      (2)1 2

      2 |4

      |3 6

      |5 10

      目前指针指向1,0,0,队列头arr[1] * 2 = 4, arr[0] 3 = 3, arr[0] 5 = 5

      (3)1 2 3

      2| 4 6

      3|6 9

      |5 10 15

      目前指针指向1,1,0,队列头arr[1] * 2 = 4, arr[1] 3 = 6, arr[0] 5 = 5

      ………………

    代码:

    import java.util.List;
    import java.util.ArrayList;
    import java.util.Scanner;

    public class {
    public static void main(String[] args) {
    Scanner in=new Scanner(System.in);
    int N=in.nextInt();
    int num=GetUglyNumber_Solution(N);
    System.out.println(num);
    }

    public static int GetUglyNumber_Solution(int N) {
    if(N<=0) return 0;
    List<Integer> list=new ArrayList<Integer>();
    list.add(1);
    int i1=0,i2=0,i3=0;
    while(list.size()<N) {
    int n1=list.get(i1)*2;
    int n2=list.get(i2)*3;
    int n3=list.get(i3)*5;
    int min=Math.min(n1, Math.min(n2, n3));
    list.add(min);
    if(min==n1) {
    i1++;
    }
    if(min==n2) {
    i2++;
    }
    if(min==n3) {
    i3++;
    }
    }
    return list.get(N-1);
    }
    }

    运行时间:26ms

    占用内存:9468k

    参考思路:https://www.nowcoder.com/questionTerminal/6aa9e04fc3794f68acf8778237ba065b


  • 相关阅读:
    C51学习 之 中断
    C51学习 之 动态数码管
    C51学习 之 LED流水灯
    锁存器 工作功能
    keil 5下载地址
    成本与利润最大化问题
    记一次VS下LINK1169的错误
    合并链表
    设计推特
    线段求交点
  • 原文地址:https://www.cnblogs.com/lijianming180/p/12032697.html
Copyright © 2020-2023  润新知