题目分析:这道题目要求奇数小于正数n的质数的数量,判断质数的方法很简单,只需要循环计算数字的因数个数就行了,但是如果在这道题里面使用循环计算数字因数的方法来判断一个数是不是质数的话肯定回因为运行超时而导致无法提交成功,这里就需要重新寻找一种寻找质数的方法,或者我们可以不需要寻找质数,而是将小于n的合数全部排除,这里我们就需要埃氏筛。下面是埃氏筛的核心思想
核心思想:埃氏筛的核心思想就是一个质数的倍数肯定是合数,换句话来说,一个合数肯定是一个小于它的质数的倍数,所以我们就可以通过一个较小的质数判断出后面的合数,就不需要通过循环来对质数实现判断。使用埃氏筛来判断质数首先我们需要一个有序的数对,我们可以直接定义一个长度为n的数组,用数组的索引代表数字,这样有序数对就有了,而数组里面我们用值0和1来代表此处的索引是质数还是合数,我们首先默认所有的数都是质数,我们从第一个质数2开始判断,将质数的倍数全部都标记出来,又由于合数是小于它的质数的倍数,所以所有的合数都可以被找出,而剩下的就是我们需要计数的质数。下面是代码的书写过程
数组的定义就不需要多说了,首先我们要确定我们要确定质数用什么值代表,然后将数组里面的值全部用这个数填满,这里我推荐用0代表质数,应为整形数组里面的默认值就是0,就不需要我们去手动的将值添加进数组,这样可以简化代码的书写,如果要用其它的值代表质数的话,我们可以使用Arrays类提供的fill()方法来填满数组,也可以在一定程度上简化代码的书写。
public static void fill(int[] a,int val)//将指定的int值val分配给指定int型数组的每个元素。
然后我们就需要开始循环遍历这个数组并判断数组中的元素,数组里面的元素为0时,就代表这个数是质数,我们就需要进入另一个循环,在这个循环中我们将质数所有的倍数且小于n的数标记出来(就是将元素值标记为1或者其它值)。下面是这部分的代码
for(int i=2;i<n;i++)
{
if(allNumber[i]==0)
{
count++;//记录质数的个数
if((long)i*i<n)//i*i的大小可能会超过int所能表示的范围需要特殊判断
for(int j=i*i;j<n;j+=i)
{
allNumber[j]=1;//将i的倍数标记为合数
}
}
}
这里值得注意的是我们只需要从i的平方开始对合数标记就可以了,因为小于i的平方的合数一定是其它更小质数的倍数,在前面就会进行标记,这里不需要进行二次标记,还是就是i平方的大小需要特殊判断。
下面的整体代码
class Solution
{
public int countPrimes(int n)
{
int count=0;
int []allNumber=new int[n];
for(int i=2;i<n;i++)
{
if(allNumber[i]==0)
{
count++;//记录质数的个数
if((long)i*i<n)//i*i的大小可能回超过int所能表示的范围需要特殊判断
for(int j=i*i;j<n;j+=i)
{
allNumber[j]=1;//将i的倍数标记为合数
}
}
}
return count;
}//埃氏筛
}