目录
一、题目描述
统计所有小于非负整数 n 的质数的数量。
示例 1:
输入:n = 10
输出:4
解释:小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。
示例 2:
输入:n = 0
输出:0
示例 3:
输入:n = 1
输出:0
提示:
- 0 <= n <= 5 * 106
二、解题方法
这是一道简单题,如果不考虑优化,很快就能解决。
主要是想记录下新接触到的这个优化方法:厄拉多筛选法:
简述是:当遍历到一个数时,把这个数的倍数全部剔除掉,因为他们不可能为质数,最终没有被剔除的数就是质数;
重要的是这个方法对数的取法:
- 如果一个数没有被小于他的数剔除,那么这个数是质数,初始数设置为2;
- 同时把这个数在所求范围内的倍数标记为合数;
比如求120以内的质数数量,在遍历到2时,2没有被标记为合数,那么他是质数,同时把120以内2的倍数全部标记为合数;当遍历到4时,在遍历2时,4被标记为合数,并且4的所有的倍数都已被标记,因为所有4的倍数都是2的倍数,所以4可以直接跳过。当遍历的数是3时,3没有别标记,那么他是质数,此时需要将他的倍数进行标记,但是注意要避免重复计算,标记时直接从3*3开始,因为3*2已经在遍历2的时候被标记了。
三、代码实现
#include <bits/stdc++.h>
using namespace std;
//时间复杂度为O(n^1.5),可以通过
bool isPrime(int n) {
for (int i = 2; i * i <= n; ++i) {
if (n % i == 0) {
return false;
}
}
return true;
}
int countPrimes(int n) {
int cnt = 0;
for (int i = 2; i < n; ++i) {
if (isPrime(i)) {
cnt++;
}
}
return cnt;
}
//厄拉多塞筛法
int countPrimes1(int n) {
vector<bool> isPrime(n, true);
for (int i = 2; i * i < n; ++i) {
//当遍历到i时,把所有为i的倍数的数都标记为假
//被之前的数标记过的数可以直接跳过
//比如当i= 2时,4会被标记为假;当i=4时可以跳过,因为所有为4的倍数的数都是2的倍数,被标记过
if (!isPrime[i]) continue;
for (int j = i * i; j < n; j += i) {
isPrime[j] = false;
}
}
int cnt = 0;
for (int i = 2; i < n; ++i) {
if (isPrime[i]) {
cnt++;
}
}
return cnt;
}
int main() {
cout << countPrimes1(10);
return 0;
}