题目:
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
分析:
首先,复习下java的int型表示范围:
java的int型由4个字节组成,即32位。 减去最高位符号位,共有31位,故表示的范围是 正负2^32(10位十进制数)。
再“预习”下负数的补码表示方法。
(1),java使用2的补码这种方式来编码负数。即,除过符号位,先对每一位取反,再加1;
例如: -42就是通过取反42中所有的位00101010来表示,得到11010101,然后再加1,得到11010110,即-42 。要解码一个负数,首先取反其所有的位,然后加1。例如-42,或11010110取反后为00101001,或41,然后加1,这样就得到了42。
(2),其次,比较特殊的几个数字:+0, -0, -1;
假定有一个byte型值,0用00000000代表。在补码表示中,只是取反所有的位,即生成11111111,它代表负0。而在整数数学中,负0是无效的。所以可以使用2的补码来表示负数。当使用2的补码时,对补码加1,产生了100000000。但这时1位太靠左不能返回到byte类型的值。因此我们规定,-0和0的表示方法是一样的,-1的解码为11111111。
最后,来学习下此题的解法,上述只是基础知识点,此题使用位运算。
(1)解法一: 整数n每次无符号右移一位,检查最右边的bit是否为1来进行统计;代码如下:
public int count1(int n){
int res = 0;
while(n != 0){
res += n&1;
n>>>=1; //等效于: n += 1;
}
return res;
}
此方法需要经过32 次循环比较;稍微多;
(2)解法二:每次进行n&(n-1)的操作,例如: n=01000100, n-1= 01000011;
n&(n-1) = 01000000; 令n=n&(n-1), 继续此过程,只需要比较两次即可。代码如下:
public int count1(int n){
int res = 0;
while(n != 0){
n &= (n-1);
res++;
}
return res;
}
(3)解法三: 和解法二次数一样。通过移除n最右边1的操作:
n -= n & (~n + 1),
来统计1的个数。
代码实现如下:
public int count3(int n){
int res = 0;
while(n != 0){
n -= n & (~n + 1);
res++;
}
return res;
}
注:此题属于位运算的题目,牢记。
部分参考:https://blog.csdn.net/asd1415926/article/details/15500593