转载自: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标记不是质数了,依此类推...,该算法由希腊数学家厄拉多塞( m 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 }