LeetCode 204 计数质数(三种筛选质数方法)

题目描述
统计所有小于非负整数 n 的质数的数量。
示例 1:
输入:n = 10
输出:4
解释:小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。
示例 2:
输入:n = 0
输出:0
示例 3:
输入:n = 1
输出:0
提示:0 <= n <= 5 * 106

const int N=5e6+10;
class Solution {
public:
    int prime[N],cnt;
    bool st[N];
    int countPrimes(int n) {
        for(int i=2;i<n;i++)
        {
            if(!st[i]) prime[cnt++]=i;
            for(int j=0;prime[j]<=n/i;j++)
            {
                st[prime[j]*i]=true;
                if(i%prime[j]==0) break;
            }
        }
        return cnt;
    }
};

首先要有两个数组质数数组用于存储质数以及bool数组用于表示该数字是否是质数,一个变量cnt表示当前为止质数的个数。
朴素方法:
将质数的倍数全是标记为true,如果该该数字没有被标记过,那么cnt++,且在质数数组中加入该数字,遍历时从2开始遍历。O(nlogn)

//朴素方法
void get_primes(int n)
{
    for(int i=2;i<=n;i++)
    {
        if(!st[i]) prime[cnt++]=i;
        for(int j=i+i;j<=n;j+=i)
        {
            st[j]=true;
        }
    }
}

埃拉托斯特尼筛法
在朴素做法中有些数字被重复的标记过了,比如,2的倍数4,6,8……,但是在遍历4的时候,8还会被计算一遍,然后标记,这样重复计算的次数有很多。因此如果该数字没有被标记,那么再进行循环判断可以减少运算次数。O(nloglogn)

//埃拉托斯特尼筛法
void get_primes(int n)
{
    for(int i=2;i<=n;i++)
    {
        if(!st[i]) 
        {
            prime[cnt++]=i;
            for(int j=i+i;j<=n;j+=i) st[j]=true;
        }
       
    }
}

n只会被其最小质因子筛掉。i % pj==0 说明pj 一定是pj * i 的最小质因子;i % pj !=0 说明 pj 一定是小于 i 的所有质因子,pj 也一定是pj * i 的所有质因子。(注意,pj 是从小到大枚举的,且 pj 的范围是小于n/i ,因此不会出现i=10 prime[j]=3 i%prime[j]!=0 这种情况,因为prime[j]==2 也就是 10%2 等于0 之后就已经跳出循环了)。对于一个 x 一定存在一个最小质因子,假设pj为x最小质因子,当i<=x/pj 时,一定会被筛掉、(在 st[prime[j]*i] 处会被筛掉)。筛的时候使用最小质因子筛,每个数只有一个最小质因子,因此时间复杂度是线性的。

//线性筛选法
void get_primes(int n)
{
    for(int i=2;i<=n;i++)
    {
        if(!st[i]) prime[cnt++]=i;
        for(int j=0;prime[j]<=n/i;j++)
        {
            st[prime[j]*i]=true;
            if(i%prime[j]==0) break;//primes[j]是i 的最小质因子
        }

    }
}
上一篇:SZTUOJ 1018.素数


下一篇:GNN入门之路01