1. 程式人生 > >LeetCode 204. Count Primes(C++版)

LeetCode 204. Count Primes(C++版)

Description:

Count the number of prime numbers less than a non-negative number, n.

思路分析:

方法一:我們知道大於2的偶數肯定不是素數。所以遍歷小於n的奇數,判斷是否為素數,是,count++。

class Solution {
public:
    bool isPrime(int n){
        if(n == 1) return false;
        for(int i = 2; i <= sqrt(n); i ++){
            if(n % i == 0)
                return false;
        }
        return true;
    }
    int countPrimes(int n) {
        //首先>2的偶數肯定不是質數,只需要判斷奇數
        int cnt = 0;
        if(n > 2) cnt ++;//2是質數
        for(int i = 3; i < n; i +=2 ){
            if(isPrime(i))
                cnt ++;
        }
        return cnt;
    }
};

但是這種思路超時。

方法二:

我們前面的思路可不可以優化呢?可以的——可以在判斷是否為素數這個函式上改進。由於我們在主函式中傳進來的引數一定為奇數,所以一定不能被偶數整除,那麼可以把 i 改為從3開始每迴圈一次,增加2。

class Solution {
public:
    bool isPrime(int n){
        if(n == 1) return false;
        for(int i = 3; i <= sqrt(n); i += 2){//因為我們傳進來的數一定不是偶數,所以可以直接只判斷能都被奇數整除
            if(n % i == 0)
                return false;
        }
        return true;
    }
    int countPrimes(int n) {
        //首先>2的偶數肯定不是質數,只需要判斷奇數
        int cnt = 0;
        if(n > 2) cnt ++;//2是質數
        for(int i = 3; i < n; i +=2 ){
            if(isPrime(i))
                cnt ++;
        }
        return cnt;
    }
};

這樣就通過了。

西元前250年,希臘數學家厄拉多塞(Eeatosthese)想到了一個非常美妙的質數篩法,減少了逐一檢查每個數的的步驟,可以比較簡單的從一大堆數字之中,篩選出質數來,這方法被稱作厄拉多塞篩法(Sieve of Eeatosthese)

具體操作:先將 2~n 的各個數放入表中,然後在2的上面畫一個圓圈,然後劃去2的其他倍數;第一個既未畫圈又沒有被劃去的數是3,將它畫圈,再劃去3的其他倍數;現在既未畫圈又沒有被劃去的第一個數 是5,將它畫圈,並劃去5的其他倍數……依次類推,一直到所有小於或等於 n 的各數都畫了圈或劃去為止。這時,表中畫了圈的以及未劃去的那些數正好就是小於 n 的素數。

其實,當你要畫圈的素數的平方大於 n 時,那麼後面沒有劃去的數都是素數,就不用繼續判了。如下圖:

C++程式碼如下:

class Solution {
public:
    /*厄拉多塞篩法*/
    int countPrimes(int n) {
        int cnt = 0;
        bool *del = new bool[n];
        //vector<int> del(n);
        del[2] = false;
        for(int i = 3; i < n; i ++){ // 2的倍數全部劃去,也就是將2的倍數全部標為合數,其他全部標誌為素數
            if(i % 2 == 0)
                del[i] = true;
            else 
                del[i] = false;
        }
        
        for(int i = 3; i < n; i ++){ 
            if(!del[i]){
                if(i*i > n) break; // 當前素數的平方大於n,跳出迴圈
                for(int j = i; i*j < n; j ++){
                    del[i*j] = true;
                }
            }
        }
        for(int i = 2; i < n; i ++){
            if(!del[i]) cnt ++;
        }
        
        delete [] del;
        return cnt;
    }
};