【LeetCode-204】--计算[2,n]的所有质数--

转载自:https://leetcode-cn.com/problems/count-primes/solution/ji-shu-zhi-shu-by-leetcode-solution/

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

示例 1:

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

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

输入:n = 1
输出:0

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/count-primes
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

代码路径:

方法1:

  最简单粗暴的方法,双层遍历,没什么好讲的

  时间复杂度:O(n2)

 1     public static int countPrimes2(int n) {
 2         int count = 0;
 3         for (int i = 2; i < n; i++) {
 4             if (isPrimes(i)) {
 5                 count++;
 6             }
 7         }
 8         return count;
 9     }
10 
11     public static boolean isPrimes(int i) {
12         for (int j = 2; j < i; j++) {
13             if (i % j == 0) {
14                 return false;
15             }
16         }
17         return true;
18     }

 

 方法2:

  基于方法1的优化,在方法1的isPrimes中存在很多次的冗余的计算,在isPrimes方法中j不需要遍历到i,而只需要遍历到sqrt(n);为什么呢,假设n=12;

12 = 2 × 6
12 = 3 × 4
12 = sqrt(12) × sqrt(12)
12 = 4 × 3
12 = 6 × 2

 可以看到,后两个乘积就是前面两个反过来,反转临界点就在 sqrt(n)。

换句话说,如果在 [2,sqrt(n)] 这个区间之内没有发现可整除因子,就可以直接断定 n 是素数了,因为在区间 [sqrt(n),n] 也一定不会发现可整除因子。

时间复杂度:O(n√n)

 1     public static int countPrimes2(int n) {
 2         int count = 0;
 3         for (int i = 2; i < n; i++) {
 4             if (isPrimes(i)) {
 5                 count++;
 6             }
 7         }
 8         return count;
 9     }
10 
11     public static boolean isPrimes(int i) {
12         //j<i 优化 j*j<i
13         for (int j = 2; j * j < i; j++) {
14             if (i % j == 0) {
15                 return false;
16             }
17         }
18         return true;
19     }

方法3:

  基于方法2,我们可以思考,一开始2为质数,那么2*i(2*i<n)的所有数是不是都不是质数了,因为能被2整除,i为3,是质数,那么3*i的所有数也都不是质数了,那么4刚已经被2标记不是质数了,依此类推...,该算法由希腊数学家厄拉多塞(\rm EratosthenesEratosthenes)提出,称为厄拉多塞筛法,简称埃氏筛。

时间复杂度:O(nloglog n)

 1     public static int countPrimes(int n) {
 2         //所有数值的状态为1,假设所有数都为质数
 3         int[] isPrime = new int[n];
 4         Arrays.fill(isPrime, 1);
 5         int ans = 0;
 6         for (int i = 2; i < n; ++i) {
 7             //当isPrime[i]为1时说明,小于i的数都不能将i除尽,所以计数+1
 8             if (isPrime[i] == 1) {
 9                 ans += 1;
10                 if ((long) i * i < n) {
11                     //这里从i*i开始,因为 2*i,3*i,… 这些数一定在 i 之前就被其他数的倍数标记过了,和方法2的思想是一样的,j+=i,倍数增长嘛
12                     for (int j = i * i; j < n; j += i) {
13                         isPrime[j] = 0;
14                     }
15                 }
16             }
17         }
18         return ans;
19     }

 方法4:

  

 1     public static int countPrimes1(int n) {
 2         boolean[] isPrimes = new boolean[n];
 3         List<Integer> primes = new ArrayList<>(n);
 4         Arrays.fill(isPrimes, true);
 5 
 6         for (int i = 2; i < n; i++) {
 7             if (isPrimes[i]) {
 8                 primes.add(i);
 9             }
10             for (int j = 0; j < primes.size() && i * primes.get(j) < n; j++) {
11                 isPrimes[i * primes.get(j)] = false;
12                 if (i % primes.get(j) == 0) {
13                     break;
14                 }
15             }
16         }
17 
18         return primes.size();
19     }

 

posted on 2020-12-03 11:50  EmilZs丶  阅读(165)  评论(0编辑  收藏  举报