Leetcode题目总结-Math-如何判断素数?

  今天做了一道很有意思的题目,leetcode上的204 Count primes.

  

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


  刚看到这道题目的时候,在想....小学生的题目,于是按照常规的判断方法即只能被1和本身这个数整除的数是素数。按照这个思路我写出了第一个解决方案。


  My Solution1:

  public int countPrimes(int n) {
        if(n == 0 || n == 1) return 0;
        //0和1不适素数
        
        int count = 0;//纪录素数个数
        boolean flag = true; //判断是否为素数的标志
        for(int i = 2; i < n; i++ ){
        //遍历n里面的所有数,来判断是否为素数
            for(int j = 2; j < i; j++){
                if(i%j == 0){
                    flag = false;
                    break;
                }
            }
            if(flag) count ++;
            flag = true;
        }
        return count;
    }
}

结果gg了,显示   time limit exceeded!


于是,想了想上述判断方法,明显存在效率极低的问题。对于每个数n,其实并不需要从2判断到n-1,我们知道,一个数若可以进行因数分解,那么分解时得到的两个数一定是一个小于等于sqrt(n),一个大于等于sqrt(n),据此,上述代码中并不需要遍历到n-1,遍历到sqrt(n)即可,因为若sqrt(n)左侧找不到约数,那么右侧也一定找不到约数。


  My Solution2:

  public int countPrimes(int n) {
        if(n == 0 || n == 1) return 0;
        
        int count = 0;
        boolean flag = true;
        for(int i = 2; i < n; i++ ){
            for(int j = 2; j <= Math.sqrt(i);j++){ 
                if(i%j == 0){
                    flag = false;
                    break;
                }
            }
            if(flag) count ++;
            flag = true;
        }
        return count;
    }
}

  结果还是gg,time limit exceeded!


  这就说明最开始的思路是不对的。没办法,百度了一下这道题目用到了一个算法   埃拉托色尼筛法算法。
下面介绍一下这个算法。

1)先把1删除(现今数学界1既不是质数也不是合数)

2)读取队列中当前最小的数2,然后把2的倍数删去

3)读取队列中当前最小的数3,然后把3的倍数删去

4)读取队列中当前最小的数5,然后把5的倍数删去

5)如上所述直到需求的范围内所有的数均删除或读取(即某个素数的平方等于最后一个数


My Solution3:

public class Solution {
    public int countPrimes(int n) {
        if(n == 0 || n == 1) return 0;
        
        boolean[] notprime = new boolean[n];
        // 默认所有的元素值都会设置成false; boolean的初始值为false
        notprime[0] = true;
        notprime[1] = true;
      //前两个数都不是素数
        
        for(int i = 2; i * i < n; i++){  //当某个素数的平方等于或者大于n的时候,判断结束
            if(!notprime[i]){ //如果I是素数,那么
                for(int j = 2; i * j < n; j++){ 
                    notprime[i*j] = true;
                }
            }
        }
        
        int count = 0;
        for(boolean notP : notprime){
            if(!notP) count++;
        }
        
        return count;
        
    }
}

跑下来其实效率也不是很高,后面我发现有重复的地方:

 for(int i = 2; i * i < n; i++){
            if(!notprime[i]){ 
                for(int j = 2; i * j < n; j++){ //这里有大量的数字重复,比如当i=2时候,j =2,3,...;当i=3是,j= 2,3,..
                    notprime[i*j] = true; //此时2*3在i = 2时候已经判断过; i = 3没有必要再判断。
                }
            }
        }
        

改正为:


 for(int i = 2; i * i < n; i++){  
            if(!notprime[i]){ 
                for(int j = i; i * j < n; j++){ 
                    notprime[i*j] = true;
                }
            }
        }

 这样就好了!!!!



你可能感兴趣的:(Leedcode)