返回
LeetCode-0338:比特位计算的巅峰攻坚术:动若脱兔,攻其不备
后端
2023-10-19 04:16:32
突破困境,动态规划的锋利之刃
在探索 LeetCode-0338 的道路上,我们将携手动态规划,一种强大的算法策略,解开难题的奥秘。
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 / 2] + i % 2;
}
return ans;
}
}
算法的流程妙不可言,让我们仔细拆解:
- 初始化数据结构:
int[] ans = new int[n + 1];
这个数组存储着从 0 到 n 的所有整数的二进制表示中 1 的个数。
- 递推求解:
for (int i = 1; i <= n; i++) {
ans[i] = ans[i / 2] + i % 2;
}
我们从 1 开始逐个计算每个数字的 1 的个数。关键的递推公式在于:
- 如果 i 是偶数,那么 i 的 1 的个数等于 i / 2 的 1 的个数。
- 如果 i 是奇数,那么 i 的 1 的个数等于 i / 2 的 1 的个数加上 1。
- 返回结果:
最后,我们将数组 ans 返回作为最终结果。
算法的精髓:
动态规划的本质是利用子问题的重叠性来减少计算量。在本题中,子问题就是计算每个数字的 1 的个数。这些子问题相互重叠,因为计算一个数字的 1 的个数时,我们可以利用之前已经计算过的较小数字的 1 的个数。
算法的优点:
这种算法的时间复杂度为 O(n),因为我们只需要遍历一次从 0 到 n 的所有整数。空间复杂度为 O(n),因为我们需要存储数组 ans。
算法的改进:
如果我们注意到,对于任何数字 i,其 1 的个数最多为 i 本身的位数。因此,我们可以优化我们的算法,将数组 ans 的大小减少到 log(n) + 1。
public class Solution {
public int[] countBits(int n) {
int[] ans = new int[log(n) + 1];
for (int i = 1; i <= n; i++) {
ans[log(i) + 1] = ans[log(i)] + (i & 1);
}
return ans;
}
}
结语:
算法的世界中,挑战无处不在,但只要我们掌握了正确的方法,就可以一一破解。动态规划就像一把锋利的宝剑,让我们披荆斩棘,所向披靡。LeetCode-0338 的难题已经迎刃而解,让我们继续前进,迎接新的挑战!