题目描述
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
思路:这是一道位运算题。直观解法,原数&1,相当于判断原数的最后一位是不是1,如果相&的结果为1,’1’的计数加1。然后原数右移一位,即把倒数第二位移到最后一位,并将右移后的数&1,…,直到原数的第一位移到最后,然后再右移,原数变为0循环结束。
int numberOf1(int n){
int count=0;
while(n){
if((n&1)==1){
count++;
}
n=n>>1;
}
return count;
}
位运算的左移:在低位补0
位运算的右移:两种情况。情况一,无符号数,高位补0。情况二,有符号数,高位补符号位。
则上面的代码就有问题,如果是一个负数,右移后补1,其最后结果不为0,程序陷入死循环。
算法改进:既然左移没问题,都是补0,我们可以左移1,然后再&原数,执行效果和原数右移相同。可以把上面的代码改进如下:
int numberOf1(int n){
int count=0;
int flag=1;
while(flag){
if((n&flag)==1){
count++;
}
flag=flag<<1;
}
return count;
}
牛客网提交成功的完整代码:
public class Solution {
public static int NumberOf1(int n) {
//初始:比较最右面的是不是1
//循环:然后标志位左移1位,比较次右位是不是1
//终止:标志位移到最左面,然后再左移变为0结束
//把原来的数右移为何不可?如果是正数没问题,如果是负数,右移最高位为1,右移后次高位补1,然后最终结果为FFFFFFFF,死循环
int count=0;
int flag=1;
while(flag!=0){
if((n&flag)!=0){
count++;
}
flag=flag<<1;
}
return count;
}
public static void main(String[] args){
int n=9;
System.out.println(NumberOf1(n));
}
}
程序执行的次数与整数二进制的位数相关,如果是32位,则需要循环32次,也并不太高效。
高效的解法:原数&(原数-1)
原数-1,相当于原数的二进制表示,从右边开始第一个1变0,往右,所有的0变为1,往左,保持不变。
原数&(原数-1),相当于依次从右至左,剔除原数中的1,直到为0,这样便可以对每次计数,从而得到1的个数。
牛客网提交的代码如下:
public class Solution {
public static int NumberOf1(int n) {
//初始:1100&(1100-1),得到1000,相当于减少了一个1
//循环:n=n&(n-1)
//终止:所有1都减去,最后变为0,结束
int count=0;
while(n!=0){
count++;
n=n&(n-1);
}
return count;
}
public static void main(String[] args){
int n=9;
System.out.println(NumberOf1(n));
}
}