返回
剑指Offer 35:丑数
前端
2023-11-28 21:38:19
一、算法概述
在计算机科学中,丑数是指仅由质数 2、3 和 5 这三个质因子组成的正整数。 换句话说,一个数如果能够被 2、3 或 5 整除,则为丑数,否则不为丑数。 例如,6、8 和 15 都是丑数,而 7、11 和 21 不是丑数。
丑数的寻找是一个经典的动态规划问题,其基本思路是利用一个数组 M 记录丑数序列,并从 M 中依次取出丑数作为下一个丑数的候选数。
- 初始化数组 M
首先,我们将数组 M 的前三个元素初始化为丑数 1、2 和 3,以满足丑数序列的定义。
- 更新数组 M
接下来,我们需要不断更新数组 M,以保持其包含所有丑数。具体来说,我们从 M 中依次取出丑数作为候选数,并将其分别乘以 2、3 和 5 得到三个新的候选数。这三个候选数中,一定会有一个是最小的丑数,将其加入数组 M 即可。
- 查找下一个丑数
当数组 M 中的丑数数量达到 n 时,我们就可以从中找到第 n 个丑数。
二、JavaScript 实现
/**
* 返回第 n 个丑数。
*
* @param {number} n
* @returns {number}
*/
function nthUglyNumber(n) {
// 初始化丑数数组 M
const M = [1, 2, 3];
// 丑数的指数数组,分别对应 2、3 和 5
const indexes = [0, 0, 0];
// 丑数的乘积数组,分别对应 2、3 和 5
const factors = [2, 3, 5];
// 当丑数数组 M 中的丑数数量达到 n 时,停止循环
while (M.length < n) {
// 计算当前最小的丑数
const minUglyNumber = Math.min(...factors.map((factor, index) => factor * M[indexes[index]]));
// 将当前最小的丑数添加到数组 M 中
M.push(minUglyNumber);
// 更新丑数的指数数组
for (let i = 0; i < factors.length; i++) {
if (factors[i] * M[indexes[i]] === minUglyNumber) {
indexes[i]++;
}
}
}
// 返回第 n 个丑数
return M[n - 1];
}
console.log(nthUglyNumber(10)); // 12
console.log(nthUglyNumber(20)); // 20
三、时间复杂度分析
在最坏情况下,算法需要迭代 n 次才能找到第 n 个丑数。在每次迭代中,我们需要比较三个候选数并选择最小的丑数,这需要花费 O(1) 的时间。因此,算法的总时间复杂度为 O(n)。
四、结语
丑数算法是一个经典的动态规划问题,在实际应用中有着广泛的用途。通过利用数组 M 记录丑数序列,我们可以有效地避免重复计算,并在时间复杂度为 O(n) 的情况下求得第 n 个丑数。
我希望这篇文章能够帮助您理解 JavaScript 版的丑数算法,并为您的编程实践提供一些参考。如果您有任何问题或建议,请随时留言评论。