题目描述

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

解题思路

丑数定义:一个丑数只包含质因子2、3、5,则丑数 UN = (2 ^ x) * (3 ^ y) * (5 ^ z),即顺序(从小到大排序)靠后的某一个丑数一定是由顺序靠前的某一个丑数乘以质因子(2/3/5)获得。例如,已知1是第一个丑数,分别乘以2、3、5可得到2、3、5三个丑数,再将2、3、5这三个丑数分别乘以质因子2、3、5则可获得4、6、10、6、9、15、10、15、25九个丑数……显然,易发现此方法获取的丑数序列不仅得到重复丑数并且无序。因此,可以先列出三个队列如下操作(指针‘|’,丑数数组UN,其中UN[0] = 1):

  • 丑数数组:1
    乘以2的队列:|2
    乘以3的队列:|3
    乘以5的队列:|5
    目前三个序列的指针分别是0、0、0,三个入队元素值分别是UN[0] * 2 = 2、UN[0] * 3 = 3、UN[0] * 5 = 5,选取三个队列头部元素中最小值2进入丑数数组UN,将‘乘以2的队列’指针右移一位,同时将此最小值2乘以2、3、5分别对应放入三个队列中。
  • 丑数数组:1 2
    乘以2的队列:2 |4
    乘以3的队列:|3 6
    乘以5的队列:|5 10
    目前三个序列的指针分别是1、0、0,三个队列头部元素值分别是UN[1] * 2 = 4、UN[0] * 3 = 3、UN[0] * 5 = 5,选取三个队列头部元素中最小值3进入丑数数组UN,将‘乘以3的队列’指针右移一位,同时将此最小值3乘以2、3、5分别对应放入三个队列中。
  • 丑数数组:1 2 3
    乘以2的队列:2 |4 6
    乘以3的队列:3 |6 9
    乘以5的队列:|5 10 15
    目前三个序列的指针分别是1、1、0,三个队列头部元素值分别是UN[1] * 2 = 4、UN[1] * 3 = 6、UN[0] * 5 = 5,选取三个队列头部元素中最小值4进入丑数数组UN,将‘乘以2的队列’指针右移一位,同时将此最小值4乘以2、3、5分别对应放入三个队列中。
  • 丑数数组:1 2 3 4
    乘以2的队列:2 4 |6 8
    乘以3的队列:3 |6 9 12
    乘以5的队列:|5 10 15 20
    目前三个序列的指针分别是2、1、0,三个队列头部元素值分别是UN[2] * 2 = 6、UN[1] * 3 = 6、UN[0] * 5 = 5,选取三个队列头部元素中最小值5进入丑数数组UN,将‘乘以5的队列’指针右移一位,同时将此最小值5乘以2、3、5分别对应放入三个队列中。
  • 丑数数组:1 2 3 4 5
    乘以2的队列:2 4 |6 8 10
    乘以3的队列:3 |6 9 12 15
    乘以5的队列:5 |10 15 20 25
    目前三个序列的指针分别是2、1、1,三个队列头部元素值分别是UN[2] * 2 = 6、UN[1] * 3 = 6、UN[1] * 5 = 10,选取三个队列头部元素中最小值6进入丑数数组UN,将‘乘以2的队列’和‘乘以3的队列’指针都右移一位(因为这两个队列头部元素值均为最小值6),同时将此最小值6乘以2、3、5分别对应放入三个队列中。
  • 丑数数组:1 2 3 4 5 6
    乘以2的队列:2 4 6 |8 10 12
    乘以3的队列:3 6 |9 12 15 18
    乘以5的队列:5 |10 15 20 25 30
    目前三个序列的指针分别是3、2、1,…………

C++11 (clang++ 3.9)

class Solution {
public:
    int GetUglyNumber_Solution(int index) {
        if (index < 7) {  // 由分析可知从小到大的顺序中第i(i<7)个丑数是i
            return index;
        }
        int p2 = 0, p3 = 0, p5 = 0;  // p2、p3、p5分别是三个队列的指针
        int minNum = 1;  // minNum是三个队列头部元素中的最小值
        vector<int> UN;  // vector是一个能够存放任意类型的动态数组
        UN.push_back(minNum);  // 将minNum压入动态数组UN中,此时UN[0]=1
        while(UN.size() < index) { 
            minNum = min(UN[p2] * 2, min(UN[p3] * 3, UN[p5] * 5));  // 选取三个队列头部元素的最小值
            if(UN[p2] * 2 == minNum) p2++;  // 若最小值是‘乘以2的队列’头部元素,则此队列指针+1
            if(UN[p3] * 3 == minNum) p3++;  // 若最小值是‘乘以3的队列’头部元素,则此队列指针+1
            if(UN[p5] * 5 == minNum) p5++;  // 若最小值是‘乘以5的队列’头部元素,则此队列指针+1
            UN.push_back(minNum);  // 将选取的最小值minNum压入动态数组UN中
        }
        return minNum;
    }
};

2018校招真题(滴滴)—— 寻找丑数

  • 题目描述:把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
  • 输入描述:整数N
  • 输出描述:第N个丑数
  • 示例:输入6 ——> 输出6

Java (javac 1.8)

import java.lang.Math;
import java.util.ArrayList;
import java.util.Scanner;
public class Main {
    @SuppressWarnings("resource")
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        while (input.hasNext()) {
            int index = input.nextInt();
            if (index < 7) {
                System.out.println(index);
            }
            else {
                int p2 = 0, p3 = 0, p5 = 0;
                int minNum = 1;
                ArrayList<Integer> UN = new ArrayList<Integer>();
                UN.add(minNum);
                while (UN.size() < index) {
                    minNum = Math.min(UN.get(p2) * 2, Math.min(UN.get(p3) * 3, UN.get(p5) * 5));
                    if (minNum == UN.get(p2) * 2) p2++;
                    if (minNum == UN.get(p3) * 3) p3++;
                    if (minNum == UN.get(p5) * 5) p5++;
                    UN.add(minNum);
                }
                System.out.println(minNum);
            }
        }
    }
}