剑指offer第二版——面试题15(java)

面试题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);
	}
}

 

你可能感兴趣的:(剑指offer第二版)