目录
一、题目
比特位计数
二、代码
算做出来了吧😐
class Solution {
public int[] countBits(int n) {
int []res = new int[n+1];
for(int i=0;i<=n;i++) {
res[i] = countBit(i);
}
return res;
}
public int countBit(int num) {
int count = 0;
while(num!=0) {
if(num%2==1) {
count++;
}
num = num/2;
}
return count;
}
}
刚开始蒙住了,认为常规算int转二进制的方法会超时。然后看的答案,没想到答案第一种方法就是基于“ 对从 0 到 n 的每个整数直接计算「一比特数」”来求解的,这种方法虽然效率很低但是也能过,于是返回去重新写了一遍。
效率
三、题解
前言
这道题需要计算从 0 到 n 的每个整数的二进制表示中的 1 的数目。
部分编程语言有相应的内置函数用于计算给定的整数的二进制表示中的 1 的数目,例如 Java 的 Integer.bitCount,C++ 的 __builtin_popcount,Go 的 bits.OnesCount 等,读者可以自行尝试。下列各种方法均为不使用内置函数的解法。
为了表述简洁,下文用「一比特数」表示二进制表示中的 111 的数目。
方法一:Brian Kernighan 算法-布莱恩·柯林汉,克宁汉
最直观的做法是对从 0 到 n 的每个整数直接计算「一比特数」。每个 int 型的数都可以用 32 位二进制数表示,只要遍历其二进制表示的每一位即可得到 1 的数目。
利用 Brian Kernighan 算法,可以在一定程度上进一步提升计算速度。Brian Kernighan 算法(更多详细内容看博客“力扣-Kim”)的原理是:对于任意整数 x,令 x=x & (x−1),该运算将 x 的二进制表示的最后一个 1 变成 0。因此,对 x 重复该操作,直到 x 变成 0,则操作次数即为 x 的「一比特数」。
对于给定的 n,计算从 0 到 n 的每个整数的「一比特数」的时间都不会超过 O(logn),因此总时间复杂度为 O(nlogn)。
class Solution {
public int[] countBits(int n) {
int[] bits = new int[n + 1];
for (int i = 0; i <= n; i++) {
bits[i] = countOnes(i);
}
return bits;
}
public int countOnes(int x) {
int ones = 0;
while (x > 0) {
x &= (x - 1);
ones++;
}
return ones;
}
}
来源:力扣(LeetCode)
效率
通过利用 Brian Kernighan 算法,与最基本转换二进制相比,在一定程度上提升计算速度。(Brian Kernighan’s 算法是一种用于计算一个整数的二进制表示中有多少个1的高效算法。该算法的基本思想是每次将该整数的最右边的一个1置为0,直到该整数变为0为止。每次将1置为0的操作都会使得该整数的二进制表示中的1的个数减少1。)
复杂度分析
时间复杂度:O(nlogn)。需要对从 0 到 n 的每个整数使用计算「一比特数」,对于每个整数计算「一比特数」的时间都不会超过 O(logn)。
空间复杂度:O(1)。除了返回的数组以外,空间复杂度为常数。