这是我在面试时考官问我的一道算法题,但是当时我没有好的想法,只是用最基本的方法写出来的,现在整理一下这道题的思路:
1、不完善版:(问题一:如果把右移换成/2可以吗:不可以,因为除法的效率比移位运算的效率低; 问题二:如果输入的负数会出现什么情况:因为是右移,负数会高位补1,最后陷入死循环)
public int Method1(int binary){
int count = 0;
while(binary > 0){
if((binary&1) == 1){
count++;
}
//count += binary&1;
binary = binary >> 1;
}
return count;
}
2、标准版:通过设置标志位且让标志位左移避免陷入死循环中。
public int Method2(int binary){
int count = 0;
int flag = 1; //important
while(flag > 0){
if((binary&flag)>0){
count++;
}
flag = flag<<1;
}
return count;
}
3、进阶版:将二进制数的最右边1删掉,直到这个二进制数为0为止。
public int Method3(int binary){
int count = 0;
while(binary > 0){
count++;
binary = binary&(binary-1);
}
return count;
}
4、升级版(查表法,平行算法)
查表法:根据奇偶性来分析,如果它是偶数,那么n的二进制中1的个数与n/2中1的个数是相同的,比如4和2的二进制中都有一个1,6和3的二进制中都有两个1。为啥?因为n是由n/2左移一位而来,而移位并不会增加1的个数。如果n是奇数,那么n的二进制中1的个数是n/2中1的个数+1,比如7的二进制中有三个1,7/2 = 3的二进制中有两个1。为啥?因为当n是奇数时,n相当于n/2左移一位再加1。
对于任意一个32位无符号整数,将其分割为4部分,每部分8bit,对于这四个部分分别求出1的个数,再累加起来即可。而8bit对应2^8 = 256种01组合方式,这也是为什么表的大小为256的原因。注意类型转换的时候,先取到n的地址,然后转换为unsigned char*,这样一个unsigned int(4 bytes)对应四个unsigned char(1 bytes),分别取出来计算即可。举个例子吧,以87654321(十六进制)为例,先写成二进制形式-8bit一组,共四组,以不同颜色区分,这四组中1的个数分别为4,4,3,2,所以一共是13个1,如下面所示。
10000111 01100101 01000011 00100001 = 4 + 4 + 3 + 2 = 13
public int Method4(int binary){
int count = 0;
if(0>binary || binary>Integer.MAX_VALUE){
return -1;
}
int[] searchTable = new int[256];
for(int i=0;i<256;i++){
searchTable[i] = (i&1) + searchTable[i/2];
}
int flag = 0xff;
for(int i=0;i<4;i++){
int tmp = binary&flag;
flag = flag<<8;
count+=searchTable[tmp];
}
return count;
/*return table[n &0xff] +
table[(n >>8) &0xff] +
table[(n >>16) &0xff] +
table[(n >>24) &0xff] ;
*/
}
5、平行法:
public int Method5(int n){
n = (n &0x55555555) + ((n >>1) &0x55555555) ; //5 >> 101 149
n = (n &0x33333333) + ((n >>2) &0x33333333) ; //3 >> 011 50
n = (n &0x0f0f0f0f) + ((n >>4) &0x0f0f0f0f) ; //5
n = (n &0x00ff00ff) + ((n >>8) &0x00ff00ff) ; //5
n = (n &0x0000ffff) + ((n >>16) &0x0000ffff) ; //5
return n ;
}
附录:
int BitCount5(unsigned int n)
{
unsigned int tmp = n - ((n >>1) &033333333333) - ((n >>2) &011111111111);
return ((tmp + (tmp >>3)) &030707070707) %63;
}