小彩笔的刷题痛苦日记 —— 比特位计数

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

示例:
输入:2
输出:[0,1,1]
示例:
输入:5
输出:[0,1,1,2,1,2]
方法一:直接计算:
  • 使用x = x&x(x-1)的方式,将x的二进制的最后一个1变为0
例如:
11       1 0 1 1    例如要统计11的二进制中1的个数,使用ones作为计数器
10       1 0 1 0           
&--------------------
10       1 0 1 0         ones = 1
9        1 0 0 1
&--------------------
8        1 0 0 0         ones = 2
7        0 1 1 1
&-------------------
         0 0 0 0         ones = 3
       下一次 while(x>0) 不成立,所以退出循环
class Soultion{
	public int[] countBits(int nums){
		int[] rets = new int[nums + 1];
		for(int i = 0; i <= nums; i++){
			rets[i] = countOnes(i);  // 计算当前i这个数字的二进制中1的个数
		}
		return rets;
	}
	public int countOnes(int x){
		int ones = 0;
		while(x > 0){
			x &= (x - 1);
			ones++;
		}
		return ones;
	}
}
  • 时间复杂度: O ( k × n u m s ) O(k×nums) O(k×nums),其中 k k k是Int型的二进制位数。需要对从0到nums的每个数使用 O ( k ) O(k) O(k)的时间计算1的个数,因此是 O ( k × n u m s ) O(k×nums) O(k×nums)
方法二:动态规划——最高有效位
  • 在方法一中要对每个数遍历其二进制表示的每一位。在计算i的二进制1个数时如果0<j<i,且j的二进制1个数已知(例如j = 4,i = 5),则i只比j在二进制表示上多了一个1。
  • b i t [ i ] bit[i] bit[i]表示i的二进制1的个数,则由上述关系可表示成 b i t [ i ] = b i t [ j ] + 1 bit[i] = bit[j] + 1 bit[i]=bit[j]+1
  • 对于正整数 x x x,有 y y y是尽可能大的正整数,使 y ≤ x y≤x yx y y y是2的整数次幂(如2,4,8,16,32…),则 y y y的二进制表示中只有最高位有1,其余都是0。此时称 y y y x x x最高有效位,令 z = x − y z = x - y z=xy,显然 0 ≤ z ≤ x 0≤z≤x 0zx,则 b i t [ x ] = b i t [ z ] + 1 bit[x] = bit[z] + 1 bit[x]=bit[z]+1 x x x得二进制1的个数是 z z z的二进制1的个数 + 1
例如:  使用ones作为1个数的计数器
x = 19          1  0 0 1 1     ones = 3
y = 16          1  0 0 0 0
相减-------------------------
z = 3           0  0 0 1 1     ones = 2
bit[x] = bit[z] + 1 = 2 + 1 = 3   (秒啊~真是妙蛙种子吃了妙脆角在米奇妙妙屋门口跳进跳出妙的死去活来)
  • 接下来的任务就是判断一个正整数是不是2的正整数次幂了,使用方法一中用到的按位与。如果正整数 y y y是2的整数次幂,则 y y y的二进制只有最高位是1,其余都是0,则必定有y&(y-1) = 0
  • 使用 h i g h B i t highBit highBit表示当前的最高有效位,遍历1到num的每一个正整数 i i i
    — 如果 i & (i-1)=0,则令 h i g h t B i t = i hightBit = i hightBit=i,更新当前的最高有效位( h i g h B i t = 2 , 4 , 8 , 16 , 32.... highBit = 2,4,8,16,32.... highBit=2481632....
    i i i i − h i g h B i t i - highBit ihighBit的二进制1的个数多1,由于是从小到大遍历每个数,因此遍历到 i i i时, i − h i g h B i t i - highBit ihighBit的二进制1的个数已知,令 b i t [ i ] = b i t [ i − h i g h B i t ] + 1 bit[i] = bit[i - highBit] + 1 bit[i]=bit[ihighBit]+1
class Solution {
    public int[] countBits(int num) {
        int[] rets = new int[num + 1];
        int highBit = 0;
        for(int i = 1; i <= num; i++){
            if((i & (i - 1)) == 0){
                highBit = i;
            } 
            rets[i] = rets[i - highBit] + 1;
        }
        return rets;
    }
}
  • 时间复杂度: O ( n u m ) O(num) O(num),对于每个数,只需要 O ( 1 ) O(1) O(1)时间计算二进制1的个数。
方法三:动态规划——最低有效位
  • 对于正整数x,将其二进制右移一位,等价于将其二进制表示的最低为去掉,得到的结果是 x / 2 x/2 x/2。如果 b i t [ x / 2 ] bit[x/2] bit[x/2]已知,则可以得到 b i t [ x ] bit[x] bit[x]的值:
    — 如果x是偶数,则 b i t [ x ] = b i t [ x / 2 ] bit[x] = bit[x/2] bit[x]=bit[x/2] (偶数二进制的最低为都是0)
    — 如果x是奇数,则 b i t [ x ] = b i t [ x / 2 ] + 1 bit[x] = bit[x/2] + 1 bit[x]=bit[x/2]+1
  • 将上述两种情况合并成: b i t [ x ] bit[x] bit[x]的值等于 b i t [ x / 2 ] bit[x/2] bit[x/2]的值 加上 x x x除以2的余数。
    x / 2 x/2 x/2可以写成 x > > 1 x >> 1 x>>1
    x x x除以2的余数可以写成x & 1
  • 因此有:bit[x] = bit[x >> 1] + (x & 1)
class Solution {
    public int[] countBits(int num) {
        int[] ret = new int[num + 1];
        for(int i = 0; i <= num; i++){
            ret[i] = ret[i >> 1] + (i & 1);
        }
        return ret;
    }
}
  • 时间复杂度: O ( n u m ) O(num) O(num),对于每个数,只需要 O ( 1 ) O(1) O(1)时间计算二进制1的个数。

以上均来自Leetcode官方题解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值