剑指 Offer II 003. 前 n 个数字二进制中 1 的个数

题目:

思路:

【1】基于暴力的方式

【2】动态规划 +位运算

对于所有的数字,都可以分为奇数和偶数两种:
奇数:二进制表示中,奇数一定比前面那个偶数多一个 1,因为多的就是最低位的 1。
偶数:二进制表示中,偶数中 1 的个数一定和除以 2 之后的那个数一样多。
因为最低位是 0,除以 2 就是右移一位,也就是把那个 0 抹掉而已,所以 1 的个数是不变的。

所以采用动态规划可得一下方程:
dp[i] = dp[i-1],当i为奇数
dp[i] = dp[i/2],当i为偶数

还可进一步合并为: dp[i] = dp[i/2] + i % 2;
如dp[7] = dp[7-1] = dp[6] = dp[6/2] =  dp[3];
所以合并公式是正确的,此外
i / 2 可以通过 i >> 1 得到;
i % 2 可以通过 i & 1 得到;

 

代码展示:

暴力破解的方式:

//时间12 ms击败6.8%
//内存46.5 MB击败5%
class Solution {
    public int[] countBits(int n) {
        int[] res = new int[n+1];
        String temp;
        for (int i = 1; i <= n; i++){
            temp = Integer.toBinaryString(i);
            for(int j = 0; j < temp.length(); j++){
                if(temp.charAt(j) == '1'){
                    res[i]++;
                }
            }
        }
        return res;
    }
}

动态规划+位运算优化的方式:

//1 ms击败99.95%
//内存45.7 MB击败25.22%
//时间复杂度:O(n)
//空间复杂度:O(1),如果算上返回的结果数组的话其实应该是O(n)
class Solution {
    public int[] countBits(int n) {
        int ans[] = new int[n+1];
        for(int i = 0;i <= n;i++) {
            if((i&1)==0){ //偶数
                ans[i] = ans[i>>1];
            }else{ //奇数
                ans[i] = ans[i-1]+1;
            }
        }
        return ans;
    }
}

下面这种是合并方程式的写法

//时间2 ms击败39.49%
//内存45.4 MB击败75.67%
//时间复杂度:O(n)
//空间复杂度:O(1),如果算上返回的结果数组的话其实应该是O(n)
class Solution {
    public int[] countBits(int n) {
        int[] res = new int[n+1];
        for (int i = 0; i <= n; i++){
            res[i] = res[i >> 1] + (i & 1);
        }
        return res;
    }
}

 

posted @ 2023-01-30 14:37  忧愁的chafry  阅读(16)  评论(0编辑  收藏  举报