开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第 29 天,点击查看活动详情
问题描述
给你一个正整数 n 。
请你将 n 的值替换为 n 的 质因数 之和,重复这一过程。
- 注意,如果
n能够被某个质因数多次整除,则在求和时,应当包含这个质因数同样次数。
返回 **n **可以取到的最小值。
示例 1:
输入: n = 15
输出: 5
解释: 最开始,n = 15 。
15 = 3 * 5 ,所以 n 替换为 3 + 5 = 8 。
8 = 2 * 2 * 2 ,所以 n 替换为 2 + 2 + 2 = 6 。
6 = 2 * 3 ,所以 n 替换为 2 + 3 = 5 。
5 是 n 可以取到的最小值。
示例 2:
输入: n = 3
输出: 3
解释: 最开始,n = 3 。
3 是 n 可以取到的最小值。
提示:
2 <= n <= 10^5
思路分析
首先我们要先理解一下题目意思,题目会给我们一个整数n,我们需要将n分解成n的质因数之和,如:
n=8时,我们可以将其分解成2 * 4,同时4又可以分解成2 * 2,所以最终8应该被分解成2 * 2 * 2,n的质因数之和即为2 + 2 + 2 = 6,这时6仍可以继续分解成2 * 3,得到2 + 3 = 5,5无法继续分解,所以我们可以得到最终的结果为5。
题目要求的是返回 n 可以取到的最小值,所以遇到一个数的质因数等于本身时,此时得到的结果即为最小值,可以直接返回该结果,具体步骤如下:
- 1、获取质数列表
最小的质数是 2,且每个合数都可以分解成多个质数之和,所以我们可以从 2 开始,对质数的倍数进行标记,最后剩下的即为质数,如下我们使用primeNums数组保存得到的质数列表,nums记录每个数字是否为质数。
let nums = new Array(100005).fill(true);
const primeNums = [];
for (let i = 2; i < nums.length; i++) {
if (nums[i]) {
primeNums.push(i);
for (let j = 2; j * i < nums.length; j++) {
nums[i * j] = false;
}
}
}
- 2、递归求质数和
前面我们已经获取到了质数的列表,现在我们只需要遍历列表,判断当前数字是否可以被质数整除,可以的话继续对其进行递归分解,直到分解出的数字都为质数即可结束递归。
const getSum = (n, sum = 0) => {
if (nums[n]) return sum + n;
for (const i of primeNums) {
if (n % i == 0) {
return getSum(n / i, sum + i);
}
}
};
- 3、循环进行分解
因为我们要得到 n 可以取到的最小值,所以我们在分解之后还需继续对分解得到的数字继续进行分解,直到得到最小值(无法继续分解,或者质因数等于本身)。
while (1) {
let nn = getSum(n);
if (nn == n) break;
n = nn;
}
AC 代码
完整代码如下:
/**
* @param {number} n
* @return {number}
*/
var smallestValue = function (n) {
let nums = new Array(100005).fill(true);
const primeNums = [];
for (let i = 2; i < nums.length; i++) {
if (nums[i]) {
primeNums.push(i);
for (let j = 2; j * i < nums.length; j++) {
nums[i * j] = false;
}
}
}
const getSum = (n, sum = 0) => {
if (nums[n]) return sum + n;
for (const i of primeNums) {
if (n % i == 0) {
return getSum(n / i, sum + i);
}
}
};
while (1) {
let nn = getSum(n);
if (nn == n) break;
n = nn;
}
return n;
};
说在后面
本人为算法业余爱好者,平时只是随着兴趣偶尔刷刷题,如果上面分享有错误的地方,欢迎指出,感激不尽。