[LeetCode] Counting Bits 計數位


 

Given a non negative integer number num. For every numbers i in the range 0 ≤ i ≤ num calculate the number of 1's in their binary representation and return them as an array.

Example 1:

Input: 2
Output: [0,1,1]

Example 2:

Input: 5
Output: [0,1,1,2,1,2]

Follow up:

  • It is very easy to come up with a solution with run time O(n*sizeof(integer)). But can you do it in linear time O(n) /possibly in a single pass?
  • Space complexity should be O(n).
  • Can you do it like a boss? Do it without using any builtin function like __builtin_popcount in c++ or in any other language.
Credits:

Special thanks to @ syedee for adding this problem and creating all test cases.

 

這道題給我們一個整數n,然我們統計從0到n每個數的二進制寫法的1的個數,存入一個一維數組中返回,題目中明確表示不希望我們一個數字一個數字,一位一位的傻算,而是希望我們找出規律,而且題目中也提示了我們注意 [2-3], [4-7], [8-15] 這些區間的規律,那么我們寫出0到 15 的數的二進制和1的個數如下:

0    0000    0
-------------
1    0001    1
-------------
2    0010    1
3    0011    2
-------------
4    0100    1
5    0101    2
6    0110    2
7    0111    3
-------------
8    1000    1
9    1001    2
10   1010    2
11   1011    3
12   1100    2
13   1101    3
14   1110    3
15   1111    4

我最先看出的規律是這樣的,除去前兩個數字0個1,從2開始,2和3,是 [21, 22) 區間的,值為1和2。而4到7屬於 [22, 23) 區間的,值為 1,2,2,3,前半部分1和2和上一區間相同,2和3是上面的基礎上每個數字加1。再看8到 15,屬於 [23, 24) 區間的,同樣滿足上述規律,所以可以寫出代碼如下:

 

解法一:

class Solution {
public:
    vector<int> countBits(int num) {
        if (num == 0) return {0};
        vector<int> res{0, 1};
        int k = 2, i = 2;
        while (i <= num) {
            for (i = pow(2, k - 1); i < pow(2, k); ++i) {
                if (i > num) break;
                int t = (pow(2, k) - pow(2, k - 1)) / 2;
                if (i < pow(2, k - 1) + t) res.push_back(res[i - t]);
                else res.push_back(res[i - t] + 1);
            }
            ++k;
        }
        return res;
    }
};

 

下面來看一種投機取巧的方法,直接利用了 built-in 的函數 bitset 的 count 函數可以直接返回1的個數,題目中說了不提倡用這種方法,寫出來只是多一種思路而已:

 

解法二:

class Solution {
public:
    vector<int> countBits(int num) {
        vector<int> res;
        for (int i = 0; i <= num; ++i) {
            res.push_back(bitset<32>(i).count());
        }
        return res;
    }
};

 

下面這種方法相比第一種方法就要簡潔很多了,這個規律找的更好,規律是,從1開始,遇到偶數時,其1的個數和該偶數除以2得到的數字的1的個數相同,遇到奇數時,其1的個數等於該奇數除以2得到的數字的1的個數再加1,參見代碼如下:

 

解法三:

class Solution {
public:
    vector<int> countBits(int num) {
        vector<int> res{0};
        for (int i = 1; i <= num; ++i) {
            if (i % 2 == 0) res.push_back(res[i / 2]);
            else res.push_back(res[i / 2] + 1);
        }
        return res;
    }
};

 

下面這種方法就更加巧妙了,巧妙的利用了 i&(i - 1), 這個本來是用來判斷一個數是否是2的指數的快捷方法,比如8,二進制位 1000, 那么 8&(8-1) 為0,只要為0就是2的指數, 那么我們現在來看一下0到 15 的數字和其對應的 i&(i - 1) 值:

i    binary '1'  i&(i-1)
0    0000    0
-----------------------
1    0001    1    0000
-----------------------
2    0010    1    0000
3    0011    2    0010
-----------------------
4    0100    1    0000
5    0101    2    0100
6    0110    2    0100
7    0111    3    0110
-----------------------
8    1000    1    0000
9    1001    2    1000
10   1010    2    1000
11   1011    3    1010
12   1100    2    1000
13   1101    3    1100
14   1110    3    1100
15   1111    4    1110

我們可以發現每個i值都是 i&(i-1) 對應的值加1,這樣我們就可以寫出代碼如下:

 

解法四:

class Solution {
public:
    vector<int> countBits(int num) {
        vector<int> res(num + 1, 0);
        for (int i = 1; i <= num; ++i) {
            res[i] = res[i & (i - 1)] + 1;
        }
        return res;
    }
};

 

參考資料:

https://leetcode.com/problems/counting-bits/

https://leetcode.com/discuss/92796/four-lines-c-time-o-n-space-o-1

https://leetcode.com/discuss/92694/my-408-ms-c-solution-using-bitset

https://leetcode.com/discuss/92698/my-448ms-c-easy-solution-o-n-time-and-o-n-space

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM