1、题目描述
【JZ33】把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
知识点:穷举
难度:☆☆☆☆
2、解题思路
2.1 无脑穷举
写一个函数来判断某个数字是不是丑数:
1、这个数字先不断除以2,直到不能整除,就进入下一步;
2、第1步的结果不断除以3,直到不能整除,进入下一步;
3、第2步的结果不断除以5,直到不能整除,进入下一步;
4、判断第3步的结果是否为1,是则丑数,不是则不是丑数。
题意要求写一个函数,返回从小到大的第 N 个丑数。
1、定义一个数字 num = 1,和一个数组 result 用来装丑数;
2、判断 num 是不是丑数:
2.1 是丑数,装入 result,并且num++;
2.2 不是丑数,num++,回到第2步;
当装了 N 个丑数后,返回最后一个即可。
这种穷举只适合 N 比较小的情况,而且这个方法在牛客网中直接不通过,因为时间复杂度太高。
2.2 动态规划
首先分析本题的丑数的定义:num = 2x × 3y × 5z,x≥0,y≥0,z≥0。
当 x=y=z=0 时,就是第一个丑数:1。
因为丑数集合要求从小到大排列,通过分析,我们可以知道,第 N 个丑数,一定是第 1 到第 N-1 的丑数集合中的某一个丑数乘以 2 或乘以 3 或乘以 5 的结果。
我们设置三个指针:x、y、z:
x:表示所指丑数乘以2是下一个可能加入到丑数集合中的丑数;
y:表示所指丑数乘以3是下一个可能加入到丑数集合中的丑数;
z:表示所指丑数乘以5是下一个可能加入到丑数集合中的丑数。
这三个指针一开始:x = y = z = 0。
下面通过计算推到,最后再总结三个指针的更新标准。
定义一个 result[]
来存储丑数,result[0] = 1
。
当 N = 2时,我们要计算 result[1] 的值。
result[t2] * 2 = 1 * 2 = 2;
result[t3] * 3 = 1 * 3 = 3;
result[t5] * 5 = 1 * 5 = 5;
通过Math.min(int a, int b);
函数得出上面三个最小值为 2,且是 x 的作用,所以 result[1] = 2,x++。
当 N = 3 时,要计算 result[2] 的值。此时:x = 1,y = 0,z = 0。
result[t2] * 2 = 2 * 2 = 4;
result[t3] * 3 = 1 * 3 = 3;
result[t5] * 5 = 1 * 5 = 5;
通过Math.min(int a, int b);
函数得出上面三个最小值为 3,且是 y 的作用,所以 result[2] = 3,y++。
通过上面的计算,我们可以总结出:对于第 N 个丑数,它是前面已经计算出来的某个丑数乘以2或乘以3或乘以5的结果,那这个“某个丑数”到底是哪个丑数呢?
1、如果这个“某个丑数”是要乘以2的,那么这个“某个丑数”就是 result[x];
2、如果这个“某个丑数”是要乘以3的,那么这个“某个丑数”就是 result[y];
3、如果这个“某个丑数”是要乘以5的,那么这个“某个丑数”就是 result[z];
3、解题代码
3.1 无脑穷举
package pers.klb.jzoffer.hard;
/**
* @program: JzOffer2021
* @description: 丑数
* @author: Meumax
* @create: 2020-07-24 10:40
**/
public class UglyNumber {
public int GetUglyNumber_Solution(int index) {
if (index < 1) return 0;
int[] result = new int[index];
int num = 1;
for (int i = 0; i < index; i++) {
while (!isUglyNum(num)) num++;
result[i] = num;
num++;
}
return result[index - 1];
}
private boolean isUglyNum(int num) {
if (num < 1) return false;
while (num % 2 == 0) num = num / 2;
while (num % 3 == 0) num = num / 3;
while (num % 5 == 0) num = num / 5;
return num == 1;
}
}
3.2 动态规划
package pers.klb.jzoffer.hard;
/**
* @program: JzOffer2021
* @description: 丑数
* @author: Meumax
* @create: 2020-07-24 10:40
**/
public class UglyNumber {
public int GetUglyNumber_Solution(int index) {
if (index < 1) return 0;
int[] result = new int[index];
result[0] = 1;
int t2 = 0;
int t3 = 0;
int t5 = 0;
for (int i = 1; i < index; i++) {
result[i] = Math.min(result[t2] * 2, Math.min(result[t3] * 3, result[t5] * 5));
if (result[i] == result[t2] * 2) t2++;
if (result[i] == result[t3] * 3) t3++;
if (result[i] == result[t5] * 5) t5++;
}
return result[index - 1];
}
}
4、解题心得
编程题有时候就是数学题,当数学原理明白了,其实编程已经是最后一步了。最难的,往往是数学。