leetcode338---比特位计数

leetcode338—比特位计数

关键词:位运算 动态规划

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

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

示例 1:

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

示例 2:

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

进阶:

  • 给出时间复杂度为 O ( n ∗ s i z e o f ( i n t e g e r ) ) O(n*sizeof(integer)) O(nsizeof(integer))的解答非常容易。但你可以在线性时间 O ( n ) O(n) O(n)内用一趟扫描做到吗?
  • 要求算法的空间复杂度为 O ( n ) O(n) O(n)

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/counting-bits


解法

1. 逐个遍历求解

遍历0到n所有数字,对它们依次求解。

class Solution:
    def countBits(self, n: int) -> List[int]:
		#每个数都统计一次
        count = [0]*(n+1)
        for i in range(n+1):
            t = 0
            num = i
            while num!=0:
                num = num&(num-1)
                t += 1
            count[i] = t
        return count

复杂度分析:

  • 时间复杂度: O ( n log ⁡ n ) O(n\log n) O(nlogn),对n个整数进行依次求解,每个整数用二进制表示位数不超过 log ⁡ n \log n logn 即 while 循环次数不会超过 log ⁡ n \log n logn,所以复杂度不会超过 O ( n log ⁡ n ) O(n\log n) O(nlogn)
  • 空间复杂度:除了返回数组以外,消耗空间为常数。

2.动态规划

具体原理见 官方题解

方法1—记录2的指数项

每次遍历到2的指数项时,将其记录下来,以2的一个指数项作为一个周期,运用动态规划列出状态转移方程。
h i g h b i t = i = 2 k highbit = i = 2^k highbit=i=2k
r e s u l t [ j ] = r e s u l t [ j − h i g h b i t ] + 1 result[j] = result[j-highbit] + 1 result[j]=result[jhighbit]+1
下一个周期的1的个数相当于只比当前周期对应位置多了一个最高位的1。
判断2的指数项的方法就是利用i&(i-1)=0,因为当前只有最高位为0。

class Solution:
    def countBits(self, n: int) -> List[int]:
        #动态规划1,记录2的指数项
        count = []
        count.append(0)
        for i in range(1, n+1):
            # 只有1个1,为2的指数项
            if i&(i-1) == 0:
                highbit = i
            # count[i] = count[i-highbit]+1
            count.append(count[i-highbit]+1)
        return count

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n),每个整数只需常数时间计算。
  • 空间复杂度:除了返回数组以外,消耗空间为常数。
方法2—二进制右移

将二进制右移,会使得最低位消失。

  • 当i为偶数时,最低位为0,右移不影响1的个数。
  • 当i为奇数时,最低位为1,右移使得1的个数减少1。
class Solution:
    def countBits(self, n: int) -> List[int]:
        # 动态规划2 二进制右移
        count = [0]*(n+1)
        for i in range(n+1):
            if i%2 != 0:    
              	# i为奇数时,右移一位会导致末尾1消失,减少1个1
                count[i] = count[i>>1] + 1
            else:
                # i为偶数时,右移一位没有影响
                count[i] = count[i>>1]
        return count

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n),每个整数只需常数时间计算。
  • 空间复杂度:除了返回数组以外,消耗空间为常数。
方法3—减1再补

利用i&(i-1)只会使得最低一位1变成0的性质,列出状态转移方程:

c o u n t [ i ] = c o u n t [ i & ( i − 1 ) ] + 1 count[i] = count[i\&(i-1)]+1 count[i]=count[i&(i1)]+1

i中1的个数比i&(i-1)多一个,所以可以这样进行计算。

class Solution:
    def countBits(self, n: int) -> List[int]:
		#动态规划3,i和i-1进行与运算会将最低一位1消掉,所以最后补上就构成了状态转移方程
        count = [0]*(n+1)
        for i in range(n+1):
            count[i] = count[i&(i-1)]+1
        return count

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n),每个整数只需常数时间计算。
  • 空间复杂度:除了返回数组以外,消耗空间为常数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值