leetcode 204. 计算质数

题目描述:统计所有小于非负整数 的质数的数量。

思路:

由于素数之间不存在明确的简单关系,所以需要把所有数字取出,逐一判断是否是素数,对素数计数。所以重点在于对素数的判断方法。

方法一:对于每个数,判断它是否有大于1,并小于它自身的因数。假定num>1,且num=a*b,那么必有 min(a, b) <= sqrt( num )。

    //n是素数吗?
    bool isPrime(int n)
    {
        int max=sqrt(n);
        for(int i=2;i<=max;i++)
        {
            if(n%i==0)
                return false;
        }
        return true;
    }
    
    //素数计数
    int countPrimes(int n) {
        int count=0;
        for(int i=2;i<n;i++)
            if(isPrime(i))
                count++;
        return count;
    }

时间复杂度O(n^2),空间复杂度O(1)。测试用时 832ms。

方法二:可以构建一张表,填上所有可能是素数的数。如果找到一个素数,就把它2倍及以上的倍数从表中划掉,最后表中剩下的全是素数。

int countPrimes(int n) {
        if(n<2) return 0;
        int count=0;
        bool flag[n];
        //先假定表中包含2~n-1所有的数
        for(int i=2;i<n;i++) flag[i]=true;
        //已知2是最小的素数
        for(int i=2;i<n;i++)
        {
            //如果i是素数,划掉它的倍数
            //i*(i-1)已经作为i-1的倍数划掉了,所以只要从i*i开始考虑
            if(flag[i])
            {
                count++;
                int j=i;
                //为了防止溢出,添加条件j<=n/i
                while(j<=n/i && i*j<n)
                {
                    flag[i*j]=false;
                    j++;
                }
            }
        }
        return count;
    }

时间复杂度很难直接计算,但是可以保证在第二个for循环中,对每个数的访问次数不超过 log n(N的每个质数因子都可能导致对N的一次访问。最小的质数是2,所以访问次数最多也不超过log N)。那么时间复杂度不超过O(n*logn)。空间复杂度是O(n),测试运行时间16ms。

看其他用户算法,第二个for循环中i的最大值是 sqrt(n)。这是个很有效的优化!对于任意m<=M,假如m有最小质因数a,那么必有a <= sqrt(m) <= sqrt( M )。这样一来时间复杂度降低为不超过 O(sqrt(n) * logn)。

int countPrimes(int n) {
        if(n<=2) return 0;
        int count=0;
        bool flag[n];
        //先假定表中包含2~n-1所有的数
        for(int i=2;i<n;i++) flag[i]=true;
        flag[0]=0;
        flag[1]=0;
        //已知2是最小的素数
        for(int i=2;i<=sqrt(n-1);i++)
        {
            //如果i是素数,划掉它的倍数
            if(flag[i])
            {
                int j=i*i;
                while( j<n)
                {
                    flag[j]=false;
                    j+=i;
                }
            }
        }
        //由于第二个for循环没有遍历整个数组,无法统计所有质数,所以要再遍历一次。
        for(int i=0;i<n;i++)
            if(flag[i]) count++;
        return count;
    }

测试运行时间 12ms。

猜你喜欢

转载自blog.csdn.net/liusiweix/article/details/84445969