[LeetCode] (easy) 204. Count Primes

https://leetcode.com/problems/count-primes/

Count the number of prime numbers less than a non-negative number, n.

Example:

Input: 10
Output: 4
Explanation: There are 4 prime numbers less than 10, they are 2, 3, 5, 7.

经典的算素数问题

想到的方法一

创建一个n大小的布尔数组arr,arr[i] == false对应数字i已经被确定不是素数。遍历数组arr,如果某一位i满足arr[i]为true,则++result,同时在内层遍历所有大于i的数字,并判断其是否能被i整除,以此来排除所有以i为素因子的合数。

方法一的改进版

显然方法一占用的空间太大,因此不保存所有数字,而是仅保存找到的素数,任然遍历所有小于n的数字,对每一个数字在内层遍历当前找到的素数并用他们测试当前值是否为素数。

显然在上述方法中,针对每一个数字所需要进行的除模判断的次数是:其最小的素因子在素数表中的位置。同时还要增加一次判断

class Solution {
public:
    int countPrimes(int n) {
        int prim[1000000];
        int cnt = 0;
        bool flag;
        for(int i = 2; i < n; ++i){
            flag = true;
            for(int j = 0; j < cnt; ++j){
                if(i % prim[j] == 0){
                    flag = false;
                    break;
                }
            }
            if(flag == true){
                prim[cnt] = i;
                ++cnt;
            }
        }
        
        return cnt;
        
    }
};

在99万那个例子中TLE了,因此需要改进:

1、除法开销太大,因此“素性检测”这一行为不应该由待判断数发起,而应该由识别到的素数发起,因此使用方法一的原型思路,但是不需要对其后的每一位数字进行判断处理,只需要对它的倍数进行直接剔除,如此一来就将除法转换为了加法

2、在遍历数组时,只需要检测奇数。但是要将2作为素数预先添加进结果中,这就导致需要对n<=2的情况单独处理

3、注意到在识别到一个素数之后进行剔除时,可以直接从该素数的平方开始(因为所有小于平方的含该素因子的合数,都必然存在另一个更小的素因子,已经在之前就被剔除)。但是这就需要引入一个上界约束,确保素数小于sqrt(n),否则会发生溢出情况

4、在改进1中只需要对识别到的素数的奇数倍进行剔除,因为偶数情况已经在改进2中处理

其中只进行改进1是24ms,加入改进2、3都是20ms,加入改进4变成8ms

class Solution {
public:
    int countPrimes(int n) {
        if (n <= 2) return 0;
       vector<bool> prime(n, true);
        int result = 1;
        int upper = sqrt(n);
        for(int i = 3; i < n; i += 2){
            if(prime[i]){
                ++result;
                if(i > upper) continue;
                for(int j = i*i; j < n; j += 2*i){
                    prime[j] = false;
                }
            }
        }
        return result;
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值