每日算法27_2020-12-03

来源:题库 - 力扣 (LeetCode) (leetcode-cn.com)。侵删。

题目一

204. 计数质数

统计所有小于非负整数 n 的质数的数量。

示例 1:

输入:n = 10
输出:4
解释:小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。
示例 2:

输入:n = 0
输出:0
示例 3:

输入:n = 1
输出:0

提示:

0 <= n <= 5 * 106

我的答案暴力枚举

class Solution {
    public int countPrimes(int n) {
        int nums = 0;
        for(int i=0;i<n;i++){
            if(isPrimes(i)) nums++;
        }
        return nums;
    }

    public boolean isPrimes(int n){
        if(n < 2) return false;
        if(n == 2) return true;
        if(n % 2 == 0) return false;
         
        int topLimit = (int) Math.sqrt(n); // 根号下n,并取整
        for(int i=3; i <= topLimit; i+=2){ // 3,5,7开根后都小于3,所以都是质数
            if(n%i == 0) return false; // 9开根后为3,能被3整除所以不是质数,11开根后大于3小于4,但不能被3整除所有是质数
        }

        return true;
    }
}

复杂度分析

  • 时间复杂度:

O ( n n ) O(n\sqrt{n}) O(nn )

单个数检查的时间复杂度为
O ( n ) O(\sqrt{n}) O(n )

一共要检查 O(n)个数,因此总时间复杂度为
O ( n n ) O(n\sqrt{n}) O(nn )

  • 空间复杂度:O(1)。

官方方法二:埃氏筛

class Solution {
    public int countPrimes(int n) {
        int[] isPrime = new int[n]; // 标记是否为质数
        Arrays.fill(isPrime, 1); // 1表示为质数,0表示不是
        int ans = 0;
        for (int i = 2; i < n; ++i) { // 因为上来我们知道0和1都不是质数所以直接从2开始
            if (isPrime[i] == 1) { // 这里也是因为我们本是确定2也是质数所以才可以直接+1
                ans += 1;
                
                // 只有为质数才进行当前操作
                if ((long) i * i < n) { // 将当前i扩大一倍开是否超范围
                    for (int j = i * i; j < n; j += i) { // 不超就挨个加i得到的数
                        isPrime[j] = 0; // 都不时质数,所以置为0
                    }
                }
            }
        }
        return ans;
    }
}

复杂度分析

  • 时间复杂度:O(nloglogn)。具体证明这里不再展开,读者可以自行思考或者上网搜索,本质上是要求解

∑ p n p \sum_{p}\frac{n}{p} ppn

的和,其中 p 为质数。当然我们可以了解这个算法一个比较松的上界O(nlogn) 怎么计算,这个等价于考虑
∑ i = 1 n n i \sum_{i=1}^{n}\frac{n}{i} i=1nin
的和,而
O ( ∑ i = 1 n n i ) = O ( n ∑ i = 1 n 1 i ) O(\sum_{i=1}^{n}\frac{n}{i}) = O(n\sum_{i=1}^{n}\frac{1}{i}) O(i=1nin)=O(ni=1ni1)
,而 1 到 n 中所有数的倒数和趋近于logn,因此
O ( n ∑ i = 1 n 1 i ) = O ( n log ⁡ n ) O(n\sum_{i=1}^{n}\frac{1}{i})=O(n\log n) O(ni=1ni1)=O(nlogn)

  • 空间复杂度:O(n)。我们需要 O(n) 的空间记录每个数是否为质数。

官方方法三:线性筛

埃氏筛其实还是存在冗余的标记操作,比如对于 45 这个数,它会同时被 3,5 两个数标记为合数,因此我们优化的目标是让每个合数只被标记一次,这样时间复杂度即能保证为 O(n),这就是我们接下来要介绍的线性筛。

class Solution {
    public int countPrimes(int n) {
        List<Integer> primes = new ArrayList<Integer>(); // 放质数的集合
        int[] isPrime = new int[n]; // 仍是1为质数,0为非质数
        Arrays.fill(isPrime, 1); // 先填充完数组
        for (int i = 2; i < n; ++i) {
            if (isPrime[i] == 1) {
                primes.add(i); // 是质数就往集合中添加它
            }
            
            // 不论是否为质数都进行当前操作
            // 只标记质数集合中的数与 i 相乘的数
            for (int j = 0; j < primes.size() && i * primes.get(j) < n; ++j) {
                isPrime[i * primes.get(j)] = 0;
                if (i % primes.get(j) == 0) {
                    break; // 这就说明i不是质数,要跳出当前循环
                }
            }
            
            // 保证了每个合数(非质数)只会被其「最小的质因数」筛去,即每个合数被标记一次。
        }
        return primes.size();
    }
}

复杂度分析

  • 时间复杂度:O*(*n)。
  • 空间复杂度:O(n)。

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/count-primes/solution/ji-shu-zhi-shu-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值