给你一个整数 n
,对于 0 <= i <= n
中的每个 i
,计算其二进制表示中 1
的个数 ,返回一个长度为 n + 1
的数组 ans
作为答案。
输入:n = 5
输出:[0,1,1,2,1,2]
解释:
0 --> 0
1 --> 1
2 --> 10
3 --> 11
4 --> 100
5 --> 101
方法一: 使用Brain kernighan算法统计二进制中1的个数
Brain kernighan算法:通过x&(x-1)将x的最后一位变成0,然后不断重复相与的过程直到x变成0,统计相与的次数就是x二级制中1的个数了
class Solution {
public:
int countOnes(int x){
int ones=0;
while(x>0){
x&=(x-1);
ones++;
}
return ones;
}
vector<int> countBits(int n) {
vector<int> count;
count.resize(n+1);
for(int i=0;i<=n;i++){
count[i] = countOnes(i);
}
return count;
}
};
方法二:动态规划,最高有效位
假设我们现在要求的是数i的二进制比特位中1的个数,那么在0<=j<=i中如果存在一个数其二进制比特位加一个1就是i的二进制数,那么就i的二进制比特位中1的个数就等于:
bits[i]=bit[j]+1
那么问题就变成找到这个j,方法就是对于正整数x来说,找到小于等于x并且是2的整数次幂的最大数y,也就是找到x的最高有效位对应的数是什么,比如9的二进制数是1001,最高有效位对应的是1000也就是十进制的8,那么我们要求的j就是等于x-y,bits[9] = bits[9-8]+1
这是为什么呢?因为最高有效位对应的二进制数与要求的正整数的二进制数来说只有最高位的1是一样的其他全是0,那么我们用x-y的二进制数就比x少了最高位那个1,但是其他位数和j的二进制数是一样的,因此就可以用bits[x] = bits[x-y]+1来表示
代码如下:
class Solution {
public:
vector<int> countBits(int n) {
vector<int> bits;
bits.resize(n+1);
int hightBit=0;
for(int i=1;i<=n;i++){
if((i&(i-1))==0)
hightBit = i;
bits[i] = bits[i-hightBit] + 1;
}
return bits;
}
};
方法三:动态规划,最低有效位
第二种方法用了最高有效位的方法即是通过删除最高位的1来统计目标个数,那么最低有效位就反过来,假设现在要求x的二进制中1的个数,那么
对于正整数 x,将其二进制表示右移一位,等价于将其二进制表示的最低位去掉,得到的数是 ⌊x/2⌋,那么如果x是偶数的话,bits[x]=bits[⌊x/2⌋],如果x是奇数的话,bits[x]=bits[⌊x/2⌋]+1
class Solution {
public:
vector<int> countBits(int n) {
vector<int> bits;
bits.resize(n+1);
int hightBit=0;
for(int i=1;i<=n;i++){
bits[i] = bits[i/2] + i%2;
}
return bits;
}
};