LeetCode 第 338 题「比特位计数」
字数 1024 2025-10-26 08:33:34

我来给你讲解 LeetCode 第 338 题「比特位计数」

题目描述

给你一个整数 n,对于 0 <= i <= n 中的每个 i,计算其二进制表示中 1 的个数,返回一个长度为 n + 1 的数组 ans 作为答案。

示例 1:

输入:n = 2
输出:[0,1,1]
解释:
0 --> 0    有 0 个 1
1 --> 1    有 1 个 1  
2 --> 10   有 1 个 1

示例 2:

输入:n = 5
输出:[0,1,1,2,1,2]
解释:
0 --> 0    有 0 个 1
1 --> 1    有 1 个 1
2 --> 10   有 1 个 1
3 --> 11   有 2 个 1
4 --> 100  有 1 个 1
5 --> 101  有 2 个 1

解题思路

方法一:暴力解法(直接计算)

最直观的方法是对于每个数 i,直接计算其二进制中 1 的个数。

def countBits_naive(n):
    def count_ones(x):
        count = 0
        while x:
            count += x & 1  # 检查最低位是否为1
            x >>= 1         # 右移一位
        return count
    
    return [count_ones(i) for i in range(n + 1)]

时间复杂度: O(n log n),因为每个数需要 O(log n) 时间来计算
空间复杂度: O(1),不考虑输出数组

方法二:动态规划 + 最高有效位

观察二进制数的规律:

  • 2的幂次方数(1, 2, 4, 8...)的二进制只有1个1
  • 对于任意数 i,设最高有效位为 high_bit,则:
    count[i] = 1 + count[i - high_bit]
def countBits_highbit(n):
    result = [0] * (n + 1)
    high_bit = 0  # 当前最高有效位
    
    for i in range(1, n + 1):
        if i & (i - 1) == 0:  # 判断是否为2的幂
            high_bit = i
        result[i] = 1 + result[i - high_bit]
    
    return result

原理:

  • 当 i 是 2 的幂时,i & (i-1) == 0
  • 例如:i=4(100), high_bit=4, count[4] = 1 + count[0] = 1
  • i=5(101), count[5] = 1 + count[1] = 1 + 1 = 2

方法三:动态规划 + 最低有效位

利用右移操作:count[i] = count[i >> 1] + (i & 1)

def countBits_lowbit(n):
    result = [0] * (n + 1)
    
    for i in range(1, n + 1):
        result[i] = result[i >> 1] + (i & 1)
    
    return result

原理:

  • i >> 1 相当于去掉最低位
  • i & 1 检查最低位是否为1
  • 例如:i=5(101)
    • i >> 1 = 2(10), count[2] = 1
    • i & 1 = 1
    • count[5] = 1 + 1 = 2

方法四:动态规划 + 最低设置位(最优解)

利用 i & (i-1) 可以去掉最低位的1的特性:
count[i] = count[i & (i-1)] + 1

def countBits(n):
    result = [0] * (n + 1)
    
    for i in range(1, n + 1):
        result[i] = result[i & (i - 1)] + 1
    
    return result

原理:

  • i & (i-1) 会去掉 i 的最低位的1
  • 例如:i=5(101)
    • i & (i-1) = 5 & 4 = 101 & 100 = 100(4)
    • count[5] = count[4] + 1 = 1 + 1 = 2

复杂度分析

  • 时间复杂度: O(n),每个数只需要常数时间
  • 空间复杂度: O(1),不考虑输出数组

总结

这道题展示了动态规划在位运算中的巧妙应用。最优解利用了二进制数的特性,通过简单的位运算就能高效计算出结果。关键是要发现二进制数之间的递推关系,避免重复计算。

我来给你讲解 LeetCode 第 338 题「比特位计数」 。 题目描述 给你一个整数 n ,对于 0 <= i <= n 中的每个 i ,计算其二进制表示中 1 的个数 ,返回一个长度为 n + 1 的数组 ans 作为答案。 示例 1: 示例 2: 解题思路 方法一:暴力解法(直接计算) 最直观的方法是对于每个数 i ,直接计算其二进制中 1 的个数。 时间复杂度: O(n log n),因为每个数需要 O(log n) 时间来计算 空间复杂度: O(1),不考虑输出数组 方法二:动态规划 + 最高有效位 观察二进制数的规律: 2的幂次方数(1, 2, 4, 8...)的二进制只有1个1 对于任意数 i,设最高有效位为 high_ bit,则: count[ i] = 1 + count[ i - high_ bit ] 原理: 当 i 是 2 的幂时,i & (i-1) == 0 例如:i=4(100), high_ bit=4, count[ 4] = 1 + count[ 0 ] = 1 i=5(101), count[ 5] = 1 + count[ 1 ] = 1 + 1 = 2 方法三:动态规划 + 最低有效位 利用右移操作: count[i] = count[i >> 1] + (i & 1) 原理: i >> 1 相当于去掉最低位 i & 1 检查最低位是否为1 例如:i=5(101) i >> 1 = 2(10), count[ 2 ] = 1 i & 1 = 1 count[ 5 ] = 1 + 1 = 2 方法四:动态规划 + 最低设置位(最优解) 利用 i & (i-1) 可以去掉最低位的1的特性: count[i] = count[i & (i-1)] + 1 原理: i & (i-1) 会去掉 i 的最低位的1 例如:i=5(101) i & (i-1) = 5 & 4 = 101 & 100 = 100(4) count[ 5] = count[ 4 ] + 1 = 1 + 1 = 2 复杂度分析 时间复杂度: O(n),每个数只需要常数时间 空间复杂度: O(1),不考虑输出数组 总结 这道题展示了动态规划在位运算中的巧妙应用。最优解利用了二进制数的特性,通过简单的位运算就能高效计算出结果。关键是要发现二进制数之间的递推关系,避免重复计算。