Article / 文章
LeetCode 第338题:比特位计数
给你一个整数 n ,对于 0 <= i <= n 中的每个 i ,计算其二进制表示中 1 的个数 ,返回一个长度为 n + 1 的数组 ans 作为答案。
📖 文章摘要
本文详细解析LeetCode第338题“比特位计数”,这是一道中等难度的位运算和动态规划问题。文章提供了多种解法,包括动态规划和位运算的组合方案,包含C#、Python、C++三种语言实现,配有详细的算法分析和性能对比。适合想要提升位运算和动态规划能力的程序员。
核心知识点: 位运算、动态规划、二进制表示
难度等级: 中等
推荐人群: 具有基础算法知识,想要提升位运算能力的程序员
题目描述
给你一个整数 n ,对于 0 <= i <= n 中的每个 i ,计算其二进制表示中 1 的个数 ,返回一个长度为 n + 1 的数组 ans 作为答案。
示例
示例 1:
输入:n = 2
输出:[0,1,1]
解释:
0 --> 0
1 --> 1
2 --> 10
示例 2:
输入:n = 5
输出:[0,1,1,2,1,2]
解释:
0 --> 0
1 --> 1
2 --> 10
3 --> 11
4 --> 100
5 --> 101
提示
- 0 <= n <= 10^5
解题思路
方法一:动态规划 + 最低有效位
利用动态规划的思想,结合位运算的特性来解决问题。
关键点:
- 对于任意数字x,x>>1去掉最低位后的1的个数已经计算过
- x的1的个数等于x>>1的1的个数加上最低位是否为1
- 可以用x&1判断最低位是否为1
具体步骤:
- 创建长度为n+1的结果数组
- 从1开始遍历到n
- 对于每个数i,dp[i] = dp[i>>1] + (i&1)
- 返回结果数组
时间复杂度:O(n) 空间复杂度:O(1),不计算返回数组
方法二:动态规划 + 最高有效位
另一种动态规划思路,利用最高有效位的性质。
关键点:
- 记录当前的最高有效位b
- 当i达到b的两倍时更新b
- dp[i] = dp[i-b] + 1
图解思路
方法一:动态规划 + 最低有效位分析
| 数字 | 二进制 | 右移一位 | 最低位 | 1的个数 |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 1 | 1 | 0 | 1 | 1 |
| 2 | 10 | 1 | 0 | 1 |
| 3 | 11 | 1 | 1 | 2 |
| 4 | 100 | 10 | 0 | 1 |
| 5 | 101 | 10 | 1 | 2 |
方法二:动态规划 + 最高有效位分析
| 数字 | 二进制 | 最高位b | i-b | 1的个数 |
|---|---|---|---|---|
| 0 | 0 | 1 | - | 0 |
| 1 | 1 | 1 | 0 | 1 |
| 2 | 10 | 2 | 0 | 1 |
| 3 | 11 | 2 | 1 | 2 |
| 4 | 100 | 4 | 0 | 1 |
| 5 | 101 | 4 | 1 | 2 |
代码实现
C# 实现
public class Solution {
// 方法一:动态规划 + 最低有效位
public int[] CountBits(int n) {
int[] ans = new int[n + 1];
for (int i = 1; i <= n; i++) {
ans[i] = ans[i >> 1] + (i & 1);
}
return ans;
}
// 方法二:动态规划 + 最高有效位
public int[] CountBits2(int n) {
int[] ans = new int[n + 1];
int highBit = 0;
for (int i = 1; i <= n; i++) {
if ((i & (i - 1)) == 0) {
highBit = i;
}
ans[i] = ans[i - highBit] + 1;
}
return ans;
}
}
Python 实现
class Solution:
# 方法一:动态规划 + 最低有效位
def countBits(self, n: int) -> List[int]:
ans = [0] * (n + 1)
for i in range(1, n + 1):
ans[i] = ans[i >> 1] + (i & 1)
return ans
# 方法二:动态规划 + 最高有效位
def countBits2(self, n: int) -> List[int]:
ans = [0] * (n + 1)
high_bit = 0
for i in range(1, n + 1):
if (i & (i - 1)) == 0:
high_bit = i
ans[i] = ans[i - high_bit] + 1
return ans
C++ 实现
class Solution {
public:
// 方法一:动态规划 + 最低有效位
vector<int> countBits(int n) {
vector<int> ans(n + 1);
for (int i = 1; i <= n; i++) {
ans[i] = ans[i >> 1] + (i & 1);
}
return ans;
}
// 方法二:动态规划 + 最高有效位
vector<int> countBits2(int n) {
vector<int> ans(n + 1);
int highBit = 0;
for (int i = 1; i <= n; i++) {
if ((i & (i - 1)) == 0) {
highBit = i;
}
ans[i] = ans[i - highBit] + 1;
}
return ans;
}
};
执行结果
C# 实现
- 执行用时:84 ms
- 内存消耗:42.1 MB
Python 实现
- 执行用时:68 ms
- 内存消耗:20.8 MB
C++ 实现
- 执行用时:4 ms
- 内存消耗:7.8 MB
性能对比
| 语言 | 执行用时 | 内存消耗 | 特点 |
|---|---|---|---|
| C# | 84 ms | 42.1 MB | 代码结构清晰 |
| Python | 68 ms | 20.8 MB | 实现最简洁 |
| C++ | 4 ms | 7.8 MB | 性能最优 |
代码亮点
- 🎯 巧妙的位运算应用
- 💡 优雅的动态规划转移
- 🔍 高效的空间利用
- 🎨 清晰的实现逻辑
常见错误分析
- 🚫 忽略边界条件n=0
- 🚫 位运算优先级错误
- 🚫 动态规划状态设计不当
- 🚫 空间复杂度分析不准确
解法对比
| 解法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 |
|---|---|---|---|---|
| 最低有效位 | O(n) | O(1) | 实现简单 | 不够直观 |
| 最高有效位 | O(n) | O(1) | 思路清晰 | 代码较多 |
相关题目
- LeetCode 191. 位1的个数 - 简单
- LeetCode 461. 汉明距离 - 简单
- LeetCode 190. 颠倒二进制位 - 简单
📖 系列导航
🔥 算法专题合集 - 查看完整合集
📢 关注合集更新:点击上方合集链接,关注获取最新题解!目前已更新至第338题。
💬 互动交流
感谢大家耐心阅读到这里!希望这篇题解能够帮助你更好地理解和掌握这道算法题。
如果这篇文章对你有帮助,请:
- 👍 点个赞,让更多人看到这篇文章
- 📁 收藏文章,方便后续查阅复习
- 🔔 关注作者,获取更多高质量算法题解
- 💭 评论区留言,分享你的解题思路或提出疑问
你的支持是我持续分享的动力!
💡 一起进步:算法学习路上不孤单,欢迎一起交流学习!