题目描述
338. 比特位计数
给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。
示例 1:
输入: 2
输出: [0,1,1]
示例 2:
输入: 5
输出: [0,1,1,2,1,2]
思路及代码
解法一
观察各数字的二进制代码:
0 = 0
1 = 1
2 = 10
3 = 11
4 = 100
5 = 101
6 = 110
7 = 111
…
2、3可以由0、1在高位处添加1得出,4、5、6、7可以由0、1、2、3在高位处添加1得出,以此类推。
换言之,[0, 1]可以推出[2, 3],[0, 3]可以推出[4, 7],[0, 7]可以推出[8, 15],…
维护一个变量range控制往前参考的距离即可。
class Solution{
public int[] countBits(int num) {
int[] res = new int[num + 1];
if(num == 0)
return res;
res[0] = 0;
res[1] = 1;
int i = 2, range = 2;
outer:
while(true){
int count = 0;
while(count < range){
if(i > num)
break outer;
res[i] = res[i - range] + 1;
count++;
i++;
}
range <<= 1;
}
return res;
}
}
解法二
观察一个数的二进制和它除以二后的特征。
7 = 111变为3 = 11
8 = 1000变为4 = 100
一个数除以二相当于这个数右移一位,如果这个数最低位是1,则右移后丢失一个1,于是可以得到状态转移方程:
f(n) = f(n / 2) + (n & 1)
完整代码如下:
class Solution{
public int[] countBits(int num) {
int[] res = new int[num + 1];
for(int i=1;i<res.length;i++){
res[i] = res[i / 2] + (i & 1);
}
return res;
}
}
解法三
观察两个相邻数字的二进制代码。例如:
16 = 10000,15 = 01111, 它们进行与运算得00000
13 = 1101,12 = 1100, 它们进行与运算得1100
对于一个数,它和比他小的相邻数逐位相与,可以将其最低位的1变为0,换言之,对于一个数n,它的1的个数比n & (n - 1)多一个。
状态转移方程
f(n) = f(n & (n-1)) + 1
完整代码如下:
class Solution{
public int[] countBits(int num) {
int[] res = new int[num + 1];
for(int i=1;i<res.length;i++){
res[i] = res[i