问题描述:设计一个算法,找出只含素因子2
,3
,5
的第 n 小的数。
问题说明:只包含因子2,3,5的正整数被称作丑数,比如4,10,12都是丑数,而7,23,111则不是丑数,另外1也是丑数。
问题分析:这里提供两种解题思路:
方法一:暴力循环。对一个数分别循环除2,3,5,若最后的结果为1即能除尽,计数器加1,最后输出第n个数即可。
这种方法虽然可以解决问题,但如果n是一个大整数,算法的计算量就非常大,算法时间就会很长,所以不推荐。
方法二:倒推。我们知道1是最小的丑数,那么一个丑数如果乘上2,3,5,得到的也会是丑数。我们可以从1开始,第一轮得到1乘2,3,5的最小值作为下一个丑数,即2,第二轮我们比较3,5乘第一个丑数和2乘上第二个丑数之间的最小值,作为第三个丑数。即,若丑数序列为 ugly,则第一个值为1,
第二个丑数为:min(2*ugly[1] , 3*ugly[1] , 5*ugly[1]),即min(2,3,5)=2
第三个丑数为:min(2*ugly[2] , 3*ugly[1] , 5*ugly[1]),即min(4,3,5)=3
第四个丑数为:min(2*ugly[2] , 3*ugly[2] , 5*ugly[1]),即min(4,6,5)=4
以此类推,最后输出第n个数即为所求。
代码实现:(方法二)
const nthUglyNumber = function (n) {
var start = 1; //初始值
var arr = [start]; //存放每一轮的丑数
if(n == 1){ //n=1的特殊情况
return 1;
}
var index = 1; //循环次数
var p2 = p3 = p5 = 0; //记录2,3,5的索引值
while(index < n){
arr.push(Math.min(2*arr[p2],3*arr[p3],5*arr[p5]));
if(arr[arr.length-1] == 2*arr[p2]){
p2++;
}
if(arr[arr.length-1] == 3*arr[p3]){
p3++;
}
if(arr[arr.length-1] == 5*arr[p5]){
p5++;
}
index++;
}
return arr[arr.length-1];
}