题目:输入一个非负数n,请计算0到n之间每个数字的二进制形式中1的个数,并输出一个数组。例如,输入的n为4,由于0,1,2,3,4的二进制形式中1的个数分别为0、1、1、2、1,因此输出数组【0,1,1,2,1】
对于很多人最先想到的暴力解法,就是使用一个for循环从0到n的每个整数i的二进制形式中1的个数。于是问题转换成如何求一个整数i的二进制形式中1的个数。
高效方法: 每次采用“i & (i-1)"将整数i的最右边的1变为零。整数i减去1,那么它最右边的1变成零。如果他的右边还有零,则右边所有的零都将变成1,而其左边的1变成零,以二进制的1100为例它减去1的结果为1011,1100和1011的位与运算正好是1000.二进制的1100最右边的1变为零,刚好结果就是1000.
public int[] countBits(int num){
int[] result = new int[num+1];//存储第i位1的数目
for(int i = 0; i <= num; i++){
int j = i;
while (j !=0){//判断所有的1是否都转变为零
result[i]++;
j = j & (j -1);//将最后一个一的位置变为零
}
}
return result;
}
代码优化:分析可知,”i & (i - 1)”将i的二进制形式中最右边的1变成0,也就是说,整数i的二进制形式中1的个数比“i & (i - 1) "的二进制形式中1的个数多1.
public int[] countBits(int num){
int[] result = new int[num+1];//存储第i位1的数目
for(int i = 0; i <= num; i++){
result[i] = result[i & (i-1)] + 1;
}
return result;
}
另外一种方法:根据“i/2“计算i的二进制形式中1的个数:
如果正整数i时一个偶数,那么i相当于将”i/2“左移一位的结果,因此偶数i和”i/2"的二进制形式中的1的个数是相同的。如果i时奇数,那么i相当于将“i/2”左移一位后再加一,所以奇数i的二进制形式中1的个数比“i/2”多一个1。
public int[] countBits(int num){
int[] result = new int[num+1];//存储第i位1的数目
for(int i = 0; i <= num; i++){
result = result[i>>1] + (i&1);
}
return result;
}
此代码段采用了“i>>1”计算“i/2”,用“i & 1”计算“i%2”。