题目描述:
题目解析:
首先我们需要了解的是long类型数据的二进制表示是怎样的。
众所周知,long占8个byte,1byte=8bit,故long占64位。在Java中,long是64位有符号的数据类型。范围是~,亦即-9,223,372,036,854,775,808 到 9,223,372,036,854,775,807。而二进制数在内存中以补码的形式存储的,正数的补码就是该数转化为二进制,负数的补码为符号位是1,其它位是对应正数的二进制的取反加一。故我们可以得到如下示例:
在回顾了基础知识以后,接下来就可以我们开始做题了~~
方法一:
最简洁的方法是使用Java中封装的现成的统计整数二进制中1的个数的方法:
public static int getNumberOfDigitZero(long value) {
return 64-Long.bitCount(value); //Long.bitCount(value):输出value的二进制中所含1的个数。而long的二进制有64位且二进制数中除了1就是0,故64-Long.bitCount(value)即所求。
}
方法二:
因为正数和负数补码的转化方式不同,故针对这种情况,我们要不就是对正数和负数的操作应该分开处理,要不就是找到一个对正数和负数都可以处理的方法。对于第一种思路我们可以对负数做一些操作,将最高位的符号位1变成0,即n & 0x7FFF_FFFF_FFFF_FFFFL,这样就可以把负数转化成正数了。转化前后唯一区别是最高位由1变成0,所以计算时0的数量时要减1。而对正数的处理就是不断除二,再根据除二的余数计算0的数量。
public static int getNumberOfDigitZero(long value) {
int count = 64;
if(value < 0) {
value = value & 0x7FFF_FFFF_FFFF_FFFFL;
count --;
}
while(value != 0){
if(value%2 == 1){
count--;
}
value /= 2;
}
return count;
}
方法三:
因为对于二进制数,除二操作与把整数右移一位在数学上是等价的,而且和1做与运算也可以达到取余操作所达到的效果。又因在计算机中,乘除法的效率比位运算低很多,所以我们可以用位运算代替乘除法。
public static int getNumberOfDigitZero(long value) {
int count = 64;
if (value < 0){
value = value & 0x7FFF_FFFF_FFFF_FFFFL;
count --;
}
while (value != 0){
if ((value & 1) == 1){
count--;
}
value >>>= 1;
}
return count;
}
注:Java的右移运算符>>,>>>:
>> :算术右移运算符,也称带符号右移。按二进制形式把所有的数字向右移动对应位数,低位移出,高位的空位补符号位,即正数补0,负数补1。左边第一位为符号位保持不变。
>>>:逻辑右移运算符,也称无符号右移。按二进制形式把所有的数字向右移动对应位数,低位移出,高位的空位补零。即直接整体右移,用0填充左侧的空位。
对于正数来说两者相同,对于负数来说不同。
方法四:
同样使用位运算,将该数使用“>>>”向右位移一位,这样就自动将该数字的二进制表示的最后一位舍弃掉。然后再将舍弃一位的数左移一位,这样恢复的数就是最后一位是0(左移“<<"是将二进制表示左边移出的数补在右边);当它和原先的数字比较时,如果相同,则表示原先舍弃的那位数字是0,不同则为1。这种方法不用考虑正负数问题了,代码更简洁:
public static int getNumberOfDigitZero(long value) {
int count = 64;
while (value != 0) {
if (value != ((value >>> 1) << 1)) {
count--;
}
value = value >>> 1;
}
return count;
}
方法五:
由方法四进一步思考,在位运算中,如果把一个整数减去1在与它本身做与运算,那么就会把该整数二进制表示中最右边一个1变成0。因此可得:
public static long getNumberOfDigitZero(long value) {
int count = 64;
while (value != 0){
value = value & (value-1);
count--;
}
return count;
}
注:这是博主第一次发文章,若有不足请多指教,欢迎大家一起讨论~~