• Medium | LeetCode 338. 比特位计数 | 动态规划 + 位运算


    338. 比特位计数

    给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。

    示例 1:

    输入: 2
    输出: [0,1,1]
    

    示例 2:

    输入: 5
    输出: [0,1,1,2,1,2]
    

    进阶:

    • 给出时间复杂度为O(n*sizeof(integer))的解答非常容易。但你可以在线性时间O(n)内用一趟扫描做到吗?
    • 要求算法的空间复杂度为O(n)
    • 你能进一步完善解法吗?要求在C++或任何其他语言中不使用任何内置函数(如 C++ 中的 __builtin_popcount)来执行此操作。

    方法一:对每个数字计算Pop Count(汉明权重)

    计算一个数字的二进制位数的方法如 Easy | LeetCode 191 | 剑指 Offer 15. 二进制中1的个数 | 位运算 所示。通过 n & (n - 1)可以把最右边的1变成0, 通过计算 n&(n-1) 操作的次数即可得到二进制中1的个数。

    public int[] countBits(int num) {
        int[] res = new int[num+1];
        for (int i = 0; i <= num; i++) {
            res[i] = popCount(i);
        }
        return res;
    }
    
    private int popCount(int num) {
        int res = 0;
        while (num != 0) {
            res++;
            num &= (num - 1);
        }
        return res;
    }
    

    方法二: 动态规划

    如方法一可以知道 数字 n 的二进制里1的位数, 比 n&(n-1) 的二进制位数 多1, 并且 n > n &(n-1), 则可以得到动态规划转移方程。

    [P(x)=P(x &(x-1))+1 ; ]

    利用此动态转移方程, 可以得到如下的代码

    public int[] countBits(int num) {
        int[] ans = new int[num + 1];
        for (int i = 1; i <= num; ++i)
          ans[i] = ans[i & (i - 1)] + 1;
        return ans;
    }
    

    方法三:

    已经某个数n的二进制1的位数, 通过将n的 高位每一位尝试设置为1 的方式, 可以得到下面的转移方程。

    [P(x+b)=P(x)+1, b=2^{m}>x ]

    依据此状态转移方程, 可以得到如下的代码:

    public int[] countBits(int num) {
        int[] ans = new int[num + 1];
        int i = 0, b = 1;
        // [0, b) is calculated
        while (b <= num) {
            // generate [b, 2b) or [b, num) from [0, b)
            while(i < b && i + b <= num){
                ans[i + b] = ans[i] + 1;
                ++i;
            }
            i = 0;   // reset i
            b <<= 1; // b = 2b
        }
        return ans;
    }
    

    方法四:

    通过将n最低位(2^0位)的数字抹掉(抹掉之后, 其二进制个数与n>>1相同), 可以得到如下的状态转移方程。

    [P(x)=P(x / 2)+(x quad mod 2) ]

    public int[] countBits(int num) {
        int[] ans = new int[num + 1];
        for (int i = 1; i <= num; ++i) {
            ans[i] = ans[i >> 1] + (i & 1);
        }
        // x / 2 is x >> 1 and x % 2 is x & 1
        return ans;
    }
    
  • 相关阅读:
    【程序2】
    【程序1】
    基数排序
    归并排序
    选择排序
    leetcode15
    leetcode221
    leetcode322
    leetcode921
    leetcode922
  • 原文地址:https://www.cnblogs.com/chenrj97/p/14327765.html
Copyright © 2020-2023  润新知