序列中不同最大公约数的数目
难度:
标签:
题目描述
给你一个由正整数组成的数组 nums
。
数字序列的 最大公约数 定义为序列中所有整数的共有约数中的最大整数。
- 例如,序列
[4,6,16]
的最大公约数是2
。
数组的一个 子序列 本质是一个序列,可以通过删除数组中的某些元素(或者不删除)得到。
- 例如,
[2,5,10]
是[1,2,1,2,4,1,5,10]
的一个子序列。
计算并返回 nums
的所有 非空 子序列中 不同 最大公约数的 数目 。
示例 1:

输入:nums = [6,10,3] 输出:5 解释:上图显示了所有的非空子序列与各自的最大公约数。 不同的最大公约数为 6 、10 、3 、2 和 1 。
示例 2:
输入:nums = [5,15,40,5,6] 输出:7
提示:
1 <= nums.length <= 105
1 <= nums[i] <= 2 * 105
代码结果
运行时间: 1482 ms, 内存: 30.5 MB
/*
题目思路:
1. 计算出 nums 数组中所有子序列的最大公约数。
2. 使用一个集合来存储不同的最大公约数。
3. 遍历 nums 数组中的每一个元素,计算包含该元素的所有子序列的最大公约数,并将其加入集合中。
4. 最后返回集合的大小,即为不同的最大公约数的数量。
5. 使用 Java Stream 进行处理。
*/
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.IntStream;
public class Solution {
// 辅助方法:计算两个数的最大公约数
public static int gcd(int a, int b) {
while (b != 0) {
int temp = b;
b = a % b;
a = temp;
}
return a;
}
public int countDifferentSubsequenceGCDs(int[] nums) {
int maxVal = Arrays.stream(nums).max().orElse(0);
Set<Integer> gcds = new HashSet<>();
IntStream.rangeClosed(1, maxVal).forEach(i -> {
int currentGCD = Arrays.stream(nums)
.filter(num -> num % i == 0)
.reduce(0, Solution::gcd);
if (currentGCD == i) {
gcds.add(i);
}
});
return gcds.size();
}
}
解释
方法:
这个题解采用了一种遍历所有可能的最大公约数的方法来解决问题。首先,它找到数组中的最大值 maxVal,因为任何子序列的最大公约数不可能大于数组中的最大值。接着,创建一个布尔数组 occured 来标记哪些数字在原数组中出现过。然后,对于每一个可能的最大公约数 i(从1到maxVal),它通过遍历所有 i 的倍数来检查这些倍数是否出现在原数组中,并计算这些倍数的最大公约数。如果在某个点最大公约数降到了 i,就意味着存在一个子序列的最大公约数正好是 i。通过这种方法,可以找出所有可能的最大公约数,最后返回这些不同最大公约数的数量。
时间复杂度:
O(maxVal log maxVal)
空间复杂度:
O(maxVal)
代码细节讲解
🦆
为什么在计算最大公约数时选择从1到maxVal进行遍历?是否有更高效的方法来减少遍历的范围?
▷🦆
在题解中提到的`如果subGcd == i,则找到一个新的GCD`的逻辑,为什么当subGcd等于i时就可以确认找到了一个新的最大公约数而中断循环?
▷🦆
在实际实现中,为什么要先检查`if occured[j]`再计算GCD,直接计算不可以吗?这样的顺序有何优势?
▷