动态规划、位运算、找规律[338] 比特位计数

解法1(暴力循环查找+局部优化):

// 338. 比特位计数
class Solution {
public:
    vector<int> countBits(int n) {
        // 创建实际容量为n + 1的容器
        vector<int> ans(n + 1);

        // 对于小于2的情况特殊处理
        if(n == 0){
            // n为0时由于int默认值为0所以可以直接返回
            return ans;
        }
        
        // 修改第二个元素为1,
        ans[1] = 1;
        
        // 记录每次遍历二进制中1的个数
        int times;
        
        // 当前遍历数字
        int nowVal;

        // 遍历从[0,n]的所有整数
        for(int i = 2; i <= n; i++){
            // 每次进行都将times赋值为0
            times = 0;
            
            // 利用倍数关系直接获取位数,可以看下面两组例子
            /*i.e.
                2 :   10
                4 :  100

                3 :  011
                6 : 0110
             */
            
            if(0 == (i & 0x01)){
                // 0 == (i & 0x01) : 保证i为偶数,也可以写成 (0 == i % 2),但一般前者的效率高些
                // 直接拿i的一半对应的值
                ans[i] = ans[i >> 1];
                
                // 直接开始下一次
                continue;
            }else if(0 == ((i - 1) & 0x01)){
                // 0 == ((i - 1) & 0x01) : 确保i - 1为偶数,则当前i的值就应该为(i - 1)对应的值 + 1
                ans[i] = ans[i >> 1] + 1;

                continue;
            }

            // 循环遍历nowVal的每个位,如果是1则times++
            nowVal = i;
            while(nowVal > 0){
                if(0x01 == (nowVal & 0x01))
                    times++;
                nowVal >>= 1;    
            }
            
            // 存储当前i对应位置的结果
            ans[i] = times;
        }

        return ans;
    }
};

解法2(找规律):

// 这个是参考的解法非常高效,在O(n)的时间复杂度内解决了问题,其实这个需要我们找到这里面的规律,如果可以找到,那理解起来就很简单了
class Solution {
public:
    vector<int> countBits(int n) {
        vector<int> ans(n + 1);

        // 0的话直接返回
        if (n == 0) return ans;
        
        // 大于0先提前对下标1进行处理
        ans[1] = 1;
        
        // 对照下面的例子,在结合代码就可以理解了
        /*i.e.
            0 --> 0
            1 --> 1
            2 --> 10
            3 --> 11
            4 --> 100
            5 --> 101
            6 --> 110
            7 --> 111
            
            这里可以推出一个通项公式:
                i 属于[0,2)时:
                    n[0] = 0, n[1] = 1
                i 属于[2,正无穷]时:
                    当为偶数时:
                        n[i] = n[i / 2] 
                    奇数时:
                        n[i] = n[i - 1] + 1 
         */
        // 直接从2开始进行
        for (int i = 2; i <= n; ++i) {

            if (0 == (i & 1))
                // i为偶数时,当它为偶数则它二进制包含1的个数等于i/2处的值
                ans[i] = ans[i >> 1];

            else 
                // 否则当前i对应的值应该为i - 1处对应的值 + 1 
                ans[i] = ans[i - 1] + 1;
        }

        return ans;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值