leetcode 204. 計算質數
阿新 • • 發佈:2018-12-02
題目描述:統計所有小於非負整數 n 的質數的數量。
思路:
由於素數之間不存在明確的簡單關係,所以需要把所有數字取出,逐一判斷是否是素數,對素數計數。所以重點在於對素數的判斷方法。
方法一:對於每個數,判斷它是否有大於1,並小於它自身的因數。假定num>1,且num=a*b,那麼必有 min(a, b) <= sqrt( num )。
//n是素數嗎? bool isPrime(int n) { int max=sqrt(n); for(int i=2;i<=max;i++) { if(n%i==0) return false; } return true; } //素數計數 int countPrimes(int n) { int count=0; for(int i=2;i<n;i++) if(isPrime(i)) count++; return count; }
時間複雜度O(n^2),空間複雜度O(1)。測試用時 832ms。
方法二:可以構建一張表,填上所有可能是素數的數。如果找到一個素數,就把它2倍及以上的倍數從表中劃掉,最後表中剩下的全是素數。
int countPrimes(int n) { if(n<2) return 0; int count=0; bool flag[n]; //先假定表中包含2~n-1所有的數 for(int i=2;i<n;i++) flag[i]=true; //已知2是最小的素數 for(int i=2;i<n;i++) { //如果i是素數,劃掉它的倍數 //i*(i-1)已經作為i-1的倍數劃掉了,所以只要從i*i開始考慮 if(flag[i]) { count++; int j=i; //為了防止溢位,新增條件j<=n/i while(j<=n/i && i*j<n) { flag[i*j]=false; j++; } } } return count; }
時間複雜度很難直接計算,但是可以保證在第二個for迴圈中,對每個數的訪問次數不超過 log n(N的每個質數因子都可能導致對N的一次訪問。最小的質數是2,所以訪問次數最多也不超過log N)。那麼時間複雜度不超過O(n*logn)。空間複雜度是O(n),測試執行時間16ms。
看其他使用者演算法,第二個for迴圈中i的最大值是 sqrt(n)。這是個很有效的優化!對於任意m<=M,假如m有最小質因數a,那麼必有a <= sqrt(m) <= sqrt( M )。這樣一來時間複雜度降低為不超過 O(sqrt(n) * logn)。
int countPrimes(int n) { if(n<=2) return 0; int count=0; bool flag[n]; //先假定表中包含2~n-1所有的數 for(int i=2;i<n;i++) flag[i]=true; flag[0]=0; flag[1]=0; //已知2是最小的素數 for(int i=2;i<=sqrt(n-1);i++) { //如果i是素數,劃掉它的倍數 if(flag[i]) { int j=i*i; while( j<n) { flag[j]=false; j+=i; } } } //由於第二個for迴圈沒有遍歷整個陣列,無法統計所有質數,所以要再遍歷一次。 for(int i=0;i<n;i++) if(flag[i]) count++; return count; }
測試執行時間 12ms。