1. 程式人生 > >Leetcode 338 Counting Bits

Leetcode 338 Counting Bits

題目描述:

給一個非負整數n,分別求0到n的n+1個整數的二進位制表示中1的個數,結果作為陣列輸出。

這個題目可以說很簡單,無非就是每個數求一下二進位制表示中1的個數,然後放到返回的數組裡。求二進位制1的位數雖然說五花八門,但是相比於資料規模n來說,都是常量時間,對於通常的時間複雜度要求來說,幾乎不需要考慮。比如以下幾種常見的方法:

1)普通方法,移位法:

int countbit(unsigned int i){
        int num=0;
        while(i){
                if(i&1){
                        ++num;
                }
                i=i>>1;
        }
        return num;
}

2)消1計數:

int countbit(int n){
        int num=0;
        while(n){
                n&=(n-1);
                ++num;
        }
        return num;
}

3)“平行演算法”

int countbit(unsigned int n){

        n = (n &0x55555555) + ((n >>1) &0x55555555);
        n = (n &0x33333333) + ((n >>2) &0x33333333);
        n = (n &0x0f0f0f0f) + ((n >>4) &0x0f0f0f0f);
        n = (n &0x00ff00ff) + ((n >>8) &0x00ff00ff);
        n = (n &0x0000ffff) + ((n >>16) &0x0000ffff);
        return n;
}

題目後面有一個進階,明示了有一個更好的演算法,比大多數O(n*sizeof(int))的演算法要好。這其實暗示了求下一個二進位制數1的個數其實是與之前的某個二進位制數1的個數有關聯。當時有點死腦筋,其實還是對一些方法掌握得不夠熟練,也沒有去動手算一下,直接就暴力解決了。其實有個方法是非常好想的,[2^i,2^(i+1))和[0,2^i)的二進位制數之間有什麼關係?差了一個高位的1,前一個區間中每個數都是後一區間中的數在前面多加了一個二進位制位1,那麼二進位制數1的個數自然多了1個。這樣就可以分段解決後面的數字。

vector<int> countBits(int num) {
        vector<int> ret(num+1,0);
        int i=1;
        int d;
        while( (d=i*2)<=num) {
            for(int k=i; k<d; k++ ) {
                ret[k] = ret[k-i]+1;
            }
            i=d;
        }
        for( int k=i; k<=num; k++ ) {
            ret[k] = ret[k-i]+1;
        }
        return ret;
}

當我看到beats100%的程式碼的時候,真是恍然大悟,把自己學過的方法發揮的淋漓盡致:

vector<int> countBits(int num){
        vector<int> dp(num+1,0);
        for(int i=1;i<=num;++i){
                dp[i]=dp[i&(i-1)]+1;
        }
        return dp;
}

其實看到這裡就很簡單,通常用來求一個數的二進位制1的個數的演算法,其實每次計算都是把最末尾的1去掉然後計數,同時產生的一個效果就是,這個數變小了,那麼既然變小了,我們肯定之前算過,拿來用就是了,簡直太讚了!