面试题15:二进制中1的个数
题目描述:
请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。例如把9表示成二进制是1001,有2位是1,因此如果输入9,该函数输出2
基础:
1. 位运算:左移补右,右移补左;若为负数,则右移后左边补1
2. & 与运算
eg:(无符号) 6:110 3:10 6&3=10→2 6&4=100→4
方法:
一、可能引起死循环的方法
判断最右边第一位是否为1(&1=1),计数后将原数右移,直到计数完成。
问题:
①是否可以直接改为除以2:不可以,因为除法的效率更低
②输入负数的情况:如传入-1,则表达为11,右移后会因为是负数而在左边补1,重新变为11,则会陷入循环
二、常规解法
不右移输入数字,改为左移变量flag=1,每次与每一位进行比对
flag=1时,比对第一位,左移得10,比对第二位……
三、优化解法
在【二】的解法中,n的二进制有多少位就需要比对多少次,优化后的解法只需要循环1的个数的次数
1. 先把二进制数分为两种,一种是末尾为1,一种是末尾为0
2. 末尾为1的二进制数,减1后,最后一位变为了0,相当于只对最后一位取反,所以n&(n-1)只有最后一位不同,变为0
3. 末尾为0的二进制数,减1后,最右边的1变为0,该1右边的数全变为0,如1100,减1后,变为1011,第二位的1变为0,后两位的0变为1,n&(n-1)得到的为1000,即把原数的最右的1变为了0
4. 总结:把二进制数减1后得到的n-1,和原数n进行与(&)操作后,得到的是原数n在二进制表达中,最右位置的1变为0的数
所以:使用循环,每次对n减1,并count++,直到n=0
其他类似问题:
判断一个数字是不是2的整数次方——2的整数次方中只有一个1
输入两个数字m,n,需要改变m的多少位,才能得到n——先异或,后判断结果中1的数目
※ n&(n-1)消除最右的1
代码:
// 方法二
public class Q15 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("input n:");
int n = sc.nextInt();
int count = 0;
int flag = 1;
while(flag<=n) {
if((n&flag)!=0) {
count++;
}
flag = flag<<1;
}
System.out.printf("count:%d",count);
}
}
// 方法三
public class Q15_3 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("input n:");
int n = sc.nextInt();
int count = 0;
while(n!=0) {
n = n&(n-1);
count++;
}
System.out.printf("count:%d",count);
}
}