比特位计数:
给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回
示例:
输入:2
输出:[0,1,1]
示例:
输入:5
输出:[0,1,1,2,1,2]
方法一:直接计算:
- 使用
x = x&x(x-1)
的方式,将x的二进制的最后一个1变为0
例如:
11 1 0 1 1 例如要统计11的二进制中1的个数,使用ones作为计数器
10 1 0 1 0
&--------------------
10 1 0 1 0 ones = 1
9 1 0 0 1
&--------------------
8 1 0 0 0 ones = 2
7 0 1 1 1
&-------------------
0 0 0 0 ones = 3
下一次 while(x>0) 不成立,所以退出循环
class Soultion{
public int[] countBits(int nums){
int[] rets = new int[nums + 1];
for(int i = 0; i <= nums; i++){
rets[i] = countOnes(i); // 计算当前i这个数字的二进制中1的个数
}
return rets;
}
public int countOnes(int x){
int ones = 0;
while(x > 0){
x &= (x - 1);
ones++;
}
return ones;
}
}
- 时间复杂度: O ( k × n u m s ) O(k×nums) O(k×nums),其中 k k k是Int型的二进制位数。需要对从0到nums的每个数使用 O ( k ) O(k) O(k)的时间计算1的个数,因此是 O ( k × n u m s ) O(k×nums) O(k×nums)。
方法二:动态规划——最高有效位
- 在方法一中要对每个数遍历其二进制表示的每一位。在计算
i
的二进制1个数时如果0<j<i
,且j
的二进制1个数已知(例如j = 4,i = 5
),则i
只比j
在二进制表示上多了一个1。 - 令
b
i
t
[
i
]
bit[i]
bit[i]表示
i
的二进制1的个数,则由上述关系可表示成 b i t [ i ] = b i t [ j ] + 1 bit[i] = bit[j] + 1 bit[i]=bit[j]+1 - 对于正整数 x x x,有 y y y是尽可能大的正整数,使 y ≤ x y≤x y≤x且 y y y是2的整数次幂(如2,4,8,16,32…),则 y y y的二进制表示中只有最高位有1,其余都是0。此时称 y y y为 x x x的最高有效位,令 z = x − y z = x - y z=x−y,显然 0 ≤ z ≤ x 0≤z≤x 0≤z≤x,则 b i t [ x ] = b i t [ z ] + 1 bit[x] = bit[z] + 1 bit[x]=bit[z]+1;即 x x x得二进制1的个数是 z z z的二进制1的个数 + 1
例如: 使用ones作为1个数的计数器
x = 19 1 0 0 1 1 ones = 3
y = 16 1 0 0 0 0
相减-------------------------
z = 3 0 0 0 1 1 ones = 2
bit[x] = bit[z] + 1 = 2 + 1 = 3 (秒啊~真是妙蛙种子吃了妙脆角在米奇妙妙屋门口跳进跳出妙的死去活来)
- 接下来的任务就是判断一个正整数是不是2的正整数次幂了,使用方法一中用到的按位与。如果正整数
y
y
y是2的整数次幂,则
y
y
y的二进制只有最高位是1,其余都是0,则必定有
y&(y-1) = 0
- 使用
h
i
g
h
B
i
t
highBit
highBit表示当前的最高有效位,遍历1到num的每一个正整数
i
i
i:
— 如果i & (i-1)=0
,则令 h i g h t B i t = i hightBit = i hightBit=i,更新当前的最高有效位( h i g h B i t = 2 , 4 , 8 , 16 , 32.... highBit = 2,4,8,16,32.... highBit=2,4,8,16,32....)
— i i i比 i − h i g h B i t i - highBit i−highBit的二进制1的个数多1,由于是从小到大遍历每个数,因此遍历到 i i i时, i − h i g h B i t i - highBit i−highBit的二进制1的个数已知,令 b i t [ i ] = b i t [ i − h i g h B i t ] + 1 bit[i] = bit[i - highBit] + 1 bit[i]=bit[i−highBit]+1
class Solution {
public int[] countBits(int num) {
int[] rets = new int[num + 1];
int highBit = 0;
for(int i = 1; i <= num; i++){
if((i & (i - 1)) == 0){
highBit = i;
}
rets[i] = rets[i - highBit] + 1;
}
return rets;
}
}
- 时间复杂度: O ( n u m ) O(num) O(num),对于每个数,只需要 O ( 1 ) O(1) O(1)时间计算二进制1的个数。
方法三:动态规划——最低有效位
- 对于正整数
x
,将其二进制右移一位,等价于将其二进制表示的最低为去掉,得到的结果是 x / 2 x/2 x/2。如果 b i t [ x / 2 ] bit[x/2] bit[x/2]已知,则可以得到 b i t [ x ] bit[x] bit[x]的值:
— 如果x
是偶数,则 b i t [ x ] = b i t [ x / 2 ] bit[x] = bit[x/2] bit[x]=bit[x/2] (偶数二进制的最低为都是0)
— 如果x
是奇数,则 b i t [ x ] = b i t [ x / 2 ] + 1 bit[x] = bit[x/2] + 1 bit[x]=bit[x/2]+1 - 将上述两种情况合并成:
b
i
t
[
x
]
bit[x]
bit[x]的值等于
b
i
t
[
x
/
2
]
bit[x/2]
bit[x/2]的值 加上
x
x
x除以2的余数。
x / 2 x/2 x/2可以写成 x > > 1 x >> 1 x>>1
x x x除以2的余数可以写成x & 1
- 因此有:
bit[x] = bit[x >> 1] + (x & 1)
class Solution {
public int[] countBits(int num) {
int[] ret = new int[num + 1];
for(int i = 0; i <= num; i++){
ret[i] = ret[i >> 1] + (i & 1);
}
return ret;
}
}
- 时间复杂度: O ( n u m ) O(num) O(num),对于每个数,只需要 O ( 1 ) O(1) O(1)时间计算二进制1的个数。