一、题目描述
给定整数 n
,返回 所有小于非负整数 n
的质数的数量 。
示例 1:
输入:n = 10 输出:4 解释:小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。
示例 2:
输入:n = 0 输出:0
示例 3:
输入:n = 1 输出:0
提示:
0 <= n <= 5 * 10^6
二、解题思路
这是一个经典的算法问题,通常可以使用埃拉托斯特尼筛法(Sieve of Eratosthenes)来解决。埃拉托斯特尼筛法的基本思想是从最小的质数开始,将每个质数的倍数标记为非质数,剩下的就是质数。
具体步骤如下:
- 初始化一个布尔数组
isPrime
,长度为n
,并将所有值设置为true
。 - 将
isPrime[0]
和isPrime[1]
设置为false
,因为 0 和 1 不是质数。 - 从 2 开始,对于每个数字
i
,如果isPrime[i]
是true
,则将所有i
的倍数(从i*i
开始,因为小于i*i
的倍数已经在之前的步骤中被标记过了)的isPrime
值设置为false
。 - 最后,统计数组中
true
的个数,即为小于n
的质数的数量。
三、具体代码
class Solution {
public int countPrimes(int n) {
if (n <= 2) {
return 0;
}
boolean[] isPrime = new boolean[n];
Arrays.fill(isPrime, true);
isPrime[0] = false;
isPrime[1] = false;
for (int i = 2; i * i < n; i++) {
if (isPrime[i]) {
for (int j = i * i; j < n; j += i) {
isPrime[j] = false;
}
}
}
int count = 0;
for (boolean prime : isPrime) {
if (prime) {
count++;
}
}
return count;
}
}
这段代码会正确计算出小于 n
的质数的数量。注意,循环中的 i * i < n
是因为小于 i * i
的倍数已经在之前的步骤中被标记过了,所以从 i * i
开始标记即可。这样可以减少循环次数,提高算法效率。
四、时间复杂度和空间复杂度
1. 时间复杂度
- 初始化布尔数组
isPrime
需要遍历一次数组,时间复杂度为 O(n)。 - 外层循环中,变量
i
从 2 增加到 sqrt(n),因此外层循环的次数是 sqrt(n)。 - 内层循环中,变量
j
从i * i
增加到 n,每次增加i
。在最坏的情况下,内层循环的次数是 n / i。
因此,总的时间复杂度可以近似为:O(n) + O(sqrt(n)) * O(n / i)
由于 i
从 2 到 sqrt(n),平均来说 i
接近于 sqrt(n),所以内层循环的次数可以近似为 n / sqrt(n),即:O(sqrt(n)) * O(n / sqrt(n)) = O(n)
所以,总的时间复杂度是 O(n * sqrt(n))。
2. 空间复杂度
- 我们使用了一个长度为 n 的布尔数组
isPrime
来记录每个数字是否为质数。 - 除了这个数组,没有使用额外的空间。
因此,空间复杂度是 O(n)。这是因为布尔数组中的每个元素只占用 1 个 bit,但由于 Java 中的布尔类型是 boolean,它实际上占用一个字节,但在计算空间复杂度时,我们通常忽略这个常数因子,只关注与输入规模 n 成正比的部分。
五、总结知识点
-
条件语句:使用
if
语句来检查输入值n
是否小于等于 2,如果是,则直接返回 0,因为小于 2 的整数中没有质数。 -
数组初始化:使用
new boolean[n]
创建了一个布尔类型的数组isPrime
,用于标记每个数字是否为质数。 -
数组填充:使用
Arrays.fill(isPrime, true)
方法将isPrime
数组中的所有元素初始化为true
。 -
数组元素赋值:将
isPrime[0]
和isPrime[1]
设置为false
,因为 0 和 1 不是质数。 -
嵌套循环:使用了两层
for
循环来实现埃拉托斯特尼筛法。外层循环遍历每个可能的质数,内层循环标记每个质数的倍数为非质数。 -
算术运算:在内层循环中,使用
j += i
来迭代每个质数的倍数。 -
逻辑运算:使用
if (isPrime[i])
来检查当前数字是否为质数,如果是,则执行内层循环。 -
累加操作:使用一个
int
类型的变量count
来累加数组中标记为true
的元素数量,即质数的数量。 -
遍历数组:使用增强型
for
循环(for (boolean prime : isPrime)
)来遍历isPrime
数组,并统计质数的数量。 -
返回值:最后,方法返回累加的质数数量
count
。
以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。