LeetCode 338.比特位计算

129 阅读1分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

题目:给定一个整数n,现要求对在整数范围内0<=i<=n的每个元素都计算二进制表示中1的个数,并且返回数组作为其答案。

解题思路

要解答本题,首先要明白十进制数如何转换为二进制,例如对于十进制数10,我们可以用除2取余,逆序排列的方法。具体来说即:

10/2=5...05/2=2...12/2=1...01/2=0...110 / 2 = 5 ... 0 \\ 5 / 2 = 2 ... 1 \\ 2 / 2 = 1 ... 0 \\ 1 / 2 = 0 ... 1

最终可得到10的二进制表达为1010

根据上述思路,我们可以直接用整除的方法来计算1的个数,对于一个数,其对2取余只可能为0或者1,首先看对2取余是否为1,是则直接更新1的个数,之后更新当前值为用2整除后的数。否则直接更新,一直循环直到即可。可得代码如下:

public int[] countBits(int n) {
    int[] result = new int[n + 1];
    for(int i=0;i <= n;i++){
        int count = 0, cur=i;
        while(cur >> 1 != 0||cur % 2 != 0){
            if(cur % 2 == 1) count++;
            cur = cur >> 1 ;
        }
        result[i] = count;
    }
    return result;
}

因为算术运算需要转化为位运算,因此此处直接用右移代替除法,时间复杂度为O(nlogn)O(nlogn)

进阶解法

题目要求使用时间复杂度为O(n)O(n)的算法来解决该问题,那么此时就需要扫描一遍数就得到所有答案。首先想到的就是动态规划,动态规划可以根据前面的答案来获得后面的答案。

本题是否可以使用动态规划呢?我们可以先打印n=15时的答案:

[0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4]

仔细观察,2n2^n的答案永远是1,而能被2整除的,其答案和整除后的数答案一样,不能被2整除的,其答案为被2整除后的那个数答案加1的答案,最终可得代码:

public int[] countBits2(int n) {
    if(n==0) return new int[]{0};
    int[] dp = new int[n + 1];
    dp[0] = 0;
    dp[1] = 1;
    for(int i=2;i <= n;i++){
        if(i%2==0){
            dp[i] = dp[i >> 1];
        }else {
            dp[i] = dp[1] + dp[i >> 1];
        }
    }
    return dp;
}

时间复杂度为O(n)O(n)