力扣日记剑指 Offer II 003

1. 题目

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

1.1 题意

计算 0 到 n 之间的每个数的二进制表示中 1 的个数

1.2 分析

看时间复杂度,O(32n)应该能过(也就是每个数一位一位去数1的个数),知道low-bit这个运算x & (x-1) 的话时间复杂度肯定低于O(32n),具体复杂度参考题解是O(nlogn)。

0 <= n <= 10e5

但是查看底下数据范围的时候,有一个进阶提示,可以使用O(n),好家伙一下子来了兴致。
于是我列出了几行找规律。
在这里插入图片描述
发现规律了嘛?每一行,把他从中间对半分成两份,前面一份,刚巧是上一行的结果,后面一份是前面一份的结果+1。

        2^0: 1
        2^1: 1 | 2
        2^2: 1 2 | 2 3
        2^3: 1 2 2 3 | 2 3 3 4
        ......

why? 因为下一行的前半份最高位为1,次高位为0,上一行中对应的最高位为0,次高位为1,所以他们1的个数会相同;对于下一行中后半份,最高位和次高位都为1,所以后半分是前半份的结果+1。

举个例子可能更能明白(另一个情况同理举例即可,主要是帮助理解):

    			2^2: 1 2 2 3
	   对应的10进制: 4 5 6 7
        		2^3: 1 2  2  3  2  3  3  4
	   对应的10进制: 8 9 10 11 12 13 14 15
	   举例4,5和8,9:
	   		4,5对应的二进制为0100 0101
	   		8,9对应的二进制为1000 1001
	   =>下一行的前半份最高位为1,次高位为0,上一行中对应的最高位为0,次高位为1

那接下来写递推式(具体细节可以看代码,主要是清楚这个规律,得到递推):
对于前半段,从上一行抄下来: r e s [ 2 i + j ] = r e s [ 2 ( i − 1 ) + j ] res[2^i+j]=res[2^{(i-1)} + j] res[2i+j]=res[2(i1)+j]
对于后半段,从前半段抄下来并+1: r e s [ 2 i + j + 2 ( i − 1 ) ] = r e s [ 2 i + j ] + 1 res[2^i + j + 2^{(i-1)}]=res[2^i+j]+1 res[2i+j+2(i1)]=res[2i+j]+1

细节上注意边界情况。

1.3 我的解法

class Solution {
    
    
public:
    vector<int> countBits(int n) {
    
    
        // 0000 0001 0010 0011 0100 0101 0110 0111 1000
        //    0    1    2    3    4    5    6    7    8
        //    0    1    1    2    1    2    2    3    1

        // 2^0: 1
        // 2^1: 1 2
        // 2^2: 1 2 2 3
        // 2^3: 1 2 2 3 2 3 3 4
        // ......
        if(n == 0){
    
    
            return {
    
    0};
        }
        vector<int> res;
        res.emplace_back(0);
        res.emplace_back(1);

        for(int i=1; i<=( log(n)/log(2) ); i++){
    
    
            int num = pow(2,i);
            for(int j=0; j<num/2 && ( (1<<i)+j <= n ); j++){
    
    
                // ind: 2^i+j
                // copy from  2^(i-1) + j
                res.emplace_back(res[(1 << (i-1)) + j]);
            }
            for(int j=0; j<num/2 && ( ( (1<<i) + j + (1<<(i-1)) ) ) <= n ; j++){
    
    
                // ind: 2^i + j + 2^(i-1)
                // copy from: 2^i+j
                res.emplace_back(res[(1 << i) + j] + 1);
            }
        }
        return res;
    }
};

1.4 学习题解反思

我的解法:
时间复杂度O(n), 原理上遍历每个位置都可以以O(1)时间拿到结果
空间复杂度O(1) ,使用的是常量个中间变量

学习题解:
题解中通过一些一比特的变化做递推,尤其是利用low-bit运算来做的一比特递推还是很有特色的。

1.5 bug日记

居然没出bug

2. 后记

仅分享自己的想法,有意见和指点非常感谢

猜你喜欢

转载自blog.csdn.net/qq_51204877/article/details/131265488