题目描述
给定一个非负整数 n ,请计算 0 到 n 之间的每个数字的二进制表示中 1 的个数,并输出一个数组。
示例1
输入: n = 2
输出: [0,1,1]
解释:
0 --> 0
1 --> 1
2 --> 10
示例2
输入: n = 5
输出: [0,1,1,2,1,2]
解释:
0 --> 0
1 --> 1
2 --> 10
3 --> 11
4 --> 100
5 --> 101
第0种思路
最笨的方法, 用循环先将每个十进制整数先转换为二进制的形式,然后再用循环遍历这个二进制字符串,求出1的个数。再同理求出前n个数字种1的个数各是多少,时间复杂度O(n^2),代码略。
第1种思路
我最先想到的是这个思路,将每个数字和1进行与操作&,这样如果是偶数结果必为0,如果是奇数结果必为1,而且只有奇数的二进制末尾是1,所以如果是1的话就累加到sum里,然后再将这个数字右移>>,继续判断下一位。假设一个整数有k位,那么二进制形式可能有k个1,那么时间复杂度就是O(nk)。
class Solution {
public int[] countBits(int n) {
n = n + 1;
int[] arr = new int[n];
//偶数
for(int i = 0; i < n; i += 2){
int temp = i;
int sum = 0;
while(temp > 0){
if((temp&1) == 1){
sum++;
}
temp = temp >> 1;
}
arr[i] = sum;
}
for(int i = 1; i < n; i += 2){
arr[i] = arr[i - 1] + 1;
}
return arr;
}
}
第2种思路
i&(i - 1)可以将整数 i 的二进制形式中最右侧的1变为0。
解释一下:i - 1可以将 i 中最右侧的1变为0,然后该位右侧所有的0变为1,因为减1即减去00000001,i需要从末尾开始不断向前面借位。然后i&(i - 1),因为最右侧的1变为0,最右侧的0全变为1,这两部分都与原数 i 刚好相反,0&1必为0,所以最终结果就是最右侧的1变为0。
那么知道了这个操作,我们要求一个数二进制形式中1的个数,只需要每一次都将最右侧的1变为0,即最终变为0,然后看看进行了多少次操作就可以。假设一个整数有k位,那么二进制形式可能有k个1,那么时间复杂度就是O(nk)。
class Solution {
public int[] countBits(int n) {
n = n + 1;
int[] arr = new int[n];
for(int i = 0; i < n; i++){
int j = i;
while(j > 0){
arr[i]++;
j = j & (j - 1);
}
}
return arr;
}
}
第3种思路
由第2种思路我们发现,i&(i - 1)将 i 最右侧的1变为0,即比 i 少了一个1,
所以我们可以直接由前面已经求出的来算后面。这种算法无论这个整数中有多少个1,我们都是直接求出来的,即O(1),那么求n个数复杂度就是O(n)。
class Solution {
public int[] countBits(int n) {
n = n + 1;
int[] arr = new int[n];
for(int i = 1; i < n; i++){
arr[i] = arr[i & (i - 1)] + 1;
}
return arr;
}
}
第4种思路
对于一个整数 i 而言,如果它是偶数,那么相当于由 i / 2 左移得来的,并且又因为偶数的末尾一定是0,不会丢失1,所以 i 和 i / 2二进制中1的个数是相同的;如果 i 是奇数,那么可以看作由 i / 2左移后,将末尾的0再变成1得来的,因为奇数末尾一定是1,右移会丢失掉最后一个1,所以要加回来,时间复杂度同第3种思路,为O(n)。
class Solution {
public int[] countBits(int n) {
n = n + 1;
int[] arr = new int[n];
for(int i = 1; i < n; i++){
arr[i] = arr[i / 2] + i % 2;
}
return arr;
}
}
当然,为了更高效,我们可以将 i / 2改成 i >> 1,将 i % 2改为 i & 1,效果一样,但是位运算更接近底层,所以效率更高。
class Solution {
public int[] countBits(int n) {
n = n + 1;
int[] arr = new int[n];
for(int i = 1; i < n; i++){
arr[i] = arr[i >> 1] + (i & 1);
}
return arr;
}
}