LeetCode每日一题:化整为零,轻松搞定比特位计数
2024-02-11 10:12:35
深入浅出:比特位计数的奥秘
作为程序员,我们经常需要在数字和二进制世界之间穿梭。LeetCode每日一题:比特位计数(No.338)就是一道经典的位操作题目,要求我们计算给定整数中1的个数。乍一看,这似乎是一项枯燥的任务,但通过巧妙的算法,我们可以优雅地破解它。
逐位探索:逐个击破
最直观的方法是逐个检查整数的每个比特位。在二进制数中,每个比特位的值要么为0,要么为1。如果比特位为1,则它代表一个1。因此,我们可以遍历整数的二进制表示,并累加每个1的计数。
// 逐位检查算法
public int[] countBits(int num) {
int[] result = new int[num + 1];
for (int i = 0; i <= num; i++) {
int count = 0;
int n = i;
while (n > 0) {
if ((n & 1) != 0) {
count++;
}
n >>= 1;
}
result[i] = count;
}
return result;
}
哈希魔法:命中率提升
为了优化性能,我们可以利用哈希表来存储已经计算过的结果。对于给定的整数,如果它在哈希表中,我们可以直接返回其比特位计数。否则,我们使用逐位检查的方法计算计数,然后将结果存储在哈希表中。
// 哈希表算法
public int[] countBits(int num) {
int[] result = new int[num + 1];
Map<Integer, Integer> memo = new HashMap<>();
for (int i = 0; i <= num; i++) {
if (memo.containsKey(i)) {
result[i] = memo.get(i);
} else {
int count = 0;
int n = i;
while (n > 0) {
if ((n & 1) != 0) {
count++;
}
n >>= 1;
}
memo.put(i, count);
result[i] = count;
}
}
return result;
}
动态规划:化繁为简
动态规划是一种强大的技术,它可以将复杂问题分解为较小的子问题。对于比特位计数问题,我们可以观察到一个规律:给定整数i的比特位计数要么等于它前一个整数(i-1)的比特位计数,要么等于它前一个整数除以2的比特位计数。
// 动态规划算法
public int[] countBits(int num) {
int[] result = new int[num + 1];
result[0] = 0;
for (int i = 1; i <= num; i++) {
if ((i & 1) == 0) {
result[i] = result[i >> 1];
} else {
result[i] = result[i - 1] + 1;
}
}
return result;
}
常见问题解答:深入理解
- 为什么需要计算比特位计数?
比特位计数在计算机科学中有着广泛的应用,包括数据压缩、密码学和错误检测。
- 哪种算法是最佳选择?
逐位检查算法是最直接的,但效率最低。哈希表算法性能稍好,但动态规划算法在时间复杂度上是最优的。
- 如何处理负数?
LeetCode每日一题中没有提到负数,但如果需要处理负数,我们可以将它们转换为无符号整数或使用补码表示。
- 比特位计数是否与进制转换有关?
比特位计数只涉及二进制数。将整数转换为其他进制(例如十进制)需要不同的算法。
- 是否存在一种通用的比特位计数算法?
对于所有数字,上述算法都可以正确计算比特位计数。但是,对于特定情况,可能会存在更优化的算法。
总结:驾驭位操作的艺术
比特位计数是位操作中的一个经典问题,它考验着我们的算法思维和对二进制数的理解。通过逐位检查、哈希表和动态规划三种算法,我们可以优雅地解决这个问题,并加深我们对数字世界底层结构的认识。无论是为LeetCode题目做准备还是在实际项目中应用,掌握这些算法都能让我们在位操作的世界中游刃有余。