题目:
https://leetcode-cn.com/problems/counting-bits/
题解一:
将 num 转为2进制数,然后再求 "1" 的个数。
public int[] countBits(int num) {
int[] resultArr = new int[num + 1];
for (int i = 0; i <= num; i++) {
resultArr[i] = Integer.bitCount(i);
}
return resultArr;
}
题解二:
如下表,我们先将十进制和二进制写出来仔细观察。
十进制 | 二进制 | count("1") |
0 | 0 | 0 |
1 | 1 | 1 |
2 | 1 0 | 1 |
3 | 1 1 | 2 |
4 | 1 0 0 | 1 |
5 | 1 0 1 | 2 |
6 | 1 1 0 | 2 |
7 | 1 1 1 | 3 |
8 | 1 0 0 0 | 1 |
规律:
- 如果 num 为 2 的整数次幂,那么 num 的二进制数中 1 的个数一定为 1,例: 2(10)、4(100)、8(1000)。
- num 数值上可以拆分成 2^n + j(2^n 为不超过 num 的最大值)。
由此可得dp方程:dp(num) = dp(2^n) + dp(j) = dp(j) + 1
举例:
dp(1) = dp(0) + 1 = 1;
dp(2) = 1;
dp(3) = dp(2) + dp(1) = 1 + 1 = 2;
dp(4) = 1;
dp(5) = dp(4) + dp(1) = 1 + 1 = 2;
dp(6) = dp(4) + dp(2) = 1 + 1 = 2;
所以这个题的关键在于如何求出不大于 num 的最大值 x 并且x 为 2 的整数次幂。我们可以使用与运算,如果正整数 x 是 2 的整数次幂,那么 x 的二进制中最高位一定是 1,其余都是0,因此 x&(x-1) = 0。由此可见,当且仅当 x&(x-1) = 0 时,x 才是 2 的整数次幂。
public int[] countBits(int num) {
int[] resultArr = new int[num + 1];
int temp = 1;
for (int i = 1; i <= num; i++) {
if ((i & (i-1)) == 0) {
temp = i;
}
resultArr[i] = resultArr[i-temp] + 1;
}
return resultArr;
}
时间复杂度:O(num) 。每个数都只需要 O(1) 的时间计算出其比特位计数。
题解三:
对于所有的十进制数,我们可以分为两类:
1.奇数。奇数的二进制低位肯定为 1,并且一定比前面一个偶数多一个 1,因为多的就是当前低位的 1。
2.偶数。偶数的二进制低位肯定为 0,并且二进制中 1 的个数与当前数除以 2 之后的个数一样多,因为当前数除以 2,等效于二进制数向右移一位,刚好把最低位的 0 移除掉,所以 1 的个数是一样的。
public int[] countBits(int num) {
int[] resultArr = new int[num + 1];
for (int i = 1; i <= num; i++) {
if (i % 2 == 0) {
resultArr[i] = resultArr[i/2];
} else {
resultArr[i] = resultArr[i-1] + 1;
}
}
return resultArr;
}
我们可以发现其实奇数也可以表示为向右移 1 位并加上 1(因为奇数向右移一位就把低位的 1 移出去了,所以需要加上 1),所以我们可以用位运算将代码简写:
public int[] countBits(int num) {
int[] resultArr = new int[num + 1];
for (int i = 1; i <= num; i++) {
resultArr[i] = resultArr[i>>1] + (i&1);
}
return resultArr;
}
时间复杂度:O(num) 。每个数都只需要 O(1) 的时间计算出其比特位计数。
本题的解法有很多,位运算、动态规划等,虽然不是很难但是却很有意思。特别是位运算的解法,很巧妙,代码也非常简洁。