题目描述
解法1:暴力(超时)
最简单,最容易理解的办法,但是肯定会超时。
class Solution {
public int countPrimes(int n) {
int res = 0;
for (int m = 2; m < n; m++) {
boolean tag = true;
for (int i = 2; i <= Math.sqrt(m); i++) {
if(m % i == 0){
tag = false;
break;
}
}
if(tag){
++res;
}
}
return res;
}
}
解法2:厄拉多塞筛法
大概的思想如下:
比如10以内的质数有2,3,5,7,由于质数是除了1和自身之外,没有其他因数的数,所以质数是不能被除了1和自身之外的任何数整除的。所以2的倍数(4 6 8 10 …),4的倍数(8,12,16,20,…)一定不是质数。所以就根据这个方法排除就行了。
下面开始演示一遍:
- 首先从2开始,由于2是质数,所以2的倍数一定不是质数,给他排除掉
- 遍历到3,由于3是质数,所以3的倍数6 9 12 15…一定不是质数
- 遍历到4时,由于4早已被标记成不是质数,所以跳过
- 遍历到5时,由于5是质数,所以5的倍数5 10 15 20…一定不是质数
- 按照上面的方法逐一遍历,直到所有数字都被遍历过。
遍历完成后,绿色的是质数,红色的都不是质数。
代码:
class Solution {
public static boolean[] tag = new boolean[5000010];
public int countPrimes(int n) {
int sq = (int) Math.sqrt(n);
for(int i = 2;i <= sq;++i){
if(!tag[i]){
for(int j = i * i;j <= n;j += i){
tag[j] = true;
}
}
}
int res = 0;
for (int i = 2; i < n; i++) {
if(!tag[i]){
++res;
}
}
return res;
}
}
问:为什么for循环的起始条件是j = i * i
而不是j = i * 2
答:自己画一画就知道了,比如当i
遍历到7的时候,应该标记7的倍数(14,21,28,35,42,49,…)为不是素数,如果自己画一画并仔细观察,就会发现14,21,28,35,42早已被标记过了,直接从7的平方49开始标记就可以了。