1. 程式人生 > >leetcode 204. 計算質數

leetcode 204. 計算質數

題目描述:統計所有小於非負整數 的質數的數量。

思路:

由於素數之間不存在明確的簡單關係,所以需要把所有數字取出,逐一判斷是否是素數,對素數計數。所以重點在於對素數的判斷方法。

方法一:對於每個數,判斷它是否有大於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。